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 [add_exp] */
1726 i = sscanf(buf, "%6s %d %d %d", cbuf[0], &ibuf[0], &ibuf[1], &ibuf[3]);
1728 s_append_asprintf(errbuf, "line %d: the gaussian primitive info cannot be read", lineNumber);
1732 ibuf[3] = 0; /* Additional exponent (JANPA extension) */
1733 if (strcasecmp(cbuf[0], "S") == 0) {
1735 } else if (strcasecmp(cbuf[0], "P") == 0) {
1737 } else if (strcasecmp(cbuf[0], "SP") == 0) {
1739 } else if (strcasecmp(cbuf[0], "D") == 0) {
1741 } else if (strcasecmp(cbuf[0], "D5") == 0) {
1743 } else if (strcasecmp(cbuf[0], "F") == 0) {
1745 } else if (strcasecmp(cbuf[0], "F7") == 0) {
1747 } else if (strcasecmp(cbuf[0], "G") == 0) {
1749 } else if (strcasecmp(cbuf[0], "G9") == 0) {
1752 s_append_asprintf(errbuf, "line %d: the gaussian primitive type %s is unknown", lineNumber, cbuf[0]);
1756 s_append_asprintf(errbuf, "line %d: the number of primitive (%d) must be positive", lineNumber, ibuf[0]);
1759 if (ibuf[1] < 0 || ibuf[1] >= mp->natoms) {
1760 s_append_asprintf(errbuf, "line %d: the atom index (%d) is out of range", lineNumber, ibuf[1]);
1763 MoleculeAddGaussianOrbitalShell(mp, ibuf[1], ibuf[2], ibuf[0], ibuf[3]);
1765 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1770 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1771 s_append_asprintf(errbuf, "line %d: cannot read gaussian primitive coefficients", lineNumber);
1774 MoleculeAddGaussianPrimitiveCoefficients(mp, dbuf[0], dbuf[1], dbuf[2]);
1782 } else if (strcmp(buf, "!:mo_info") == 0) {
1783 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1788 if (sscanf(buf, "%6s %d %d", cbuf[0], &ibuf[0], &ibuf[1]) < 3) {
1789 s_append_asprintf(errbuf, "line %d: the MO info cannot be correctly read", lineNumber);
1792 if (strcasecmp(cbuf[0], "RHF") == 0) {
1794 } else if (strcasecmp(cbuf[0], "ROHF") == 0) {
1796 } else if (strcasecmp(cbuf[0], "UHF") == 0) {
1799 s_append_asprintf(errbuf, "line %d: unknown HF type: %s", lineNumber, cbuf[0]);
1802 if (ibuf[0] < 0 || ibuf[1] < 0) {
1803 s_append_asprintf(errbuf, "line %d: incorrect number of electrons", lineNumber);
1806 MoleculeSetMOInfo(mp, ibuf[2], ibuf[0], ibuf[1]);
1809 } else if (strcmp(buf, "!:mo_coefficients") == 0) {
1810 if (mp->bset == NULL || mp->bset->nshells == 0) {
1811 s_append_asprintf(errbuf, "line %d: the :gaussian_primitive section must come before :mo_coefficients", lineNumber);
1814 /* Count the number of components */
1815 dp = (Double *)malloc(sizeof(Double) * mp->bset->ncomps);
1817 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1822 if (sscanf(buf, "MO %d %lf", &ibuf[0], &dbuf[6]) < 2) {
1823 s_append_asprintf(errbuf, "line %d: cannot read the MO index or energy", lineNumber);
1827 s_append_asprintf(errbuf, "line %d: the MO index (%d) must be in ascending order", lineNumber, ibuf[0]);
1831 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1832 j = sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]);
1834 s_append_asprintf(errbuf, "line %d: cannot read the MO coefficients", lineNumber);
1837 for (k = 0; k < j; k++, i++) {
1838 if (i >= mp->bset->ncomps) {
1839 s_append_asprintf(errbuf, "line %d: too many MO coefficients", lineNumber);
1844 if (i >= mp->bset->ncomps)
1847 i = MoleculeSetMOCoefficients(mp, ibuf[0], dbuf[6], mp->bset->ncomps, dp);
1849 s_append_asprintf(errbuf, "line %d: cannot set MO coefficients", lineNumber);
1852 i = ibuf[0] + 1; /* For next entry */
1855 } else if (strcmp(buf, "!:graphics") == 0) {
1856 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1857 MainViewGraphic *gp = NULL;
1862 if (mp->mview == NULL)
1863 continue; /* Skip */
1865 if (strcmp(buf, "line\n") == 0) {
1866 ibuf[0] = kMainViewGraphicLine;
1867 } else if (strcmp(buf, "poly\n") == 0) {
1868 ibuf[0] = kMainViewGraphicPoly;
1869 } else if (strcmp(buf, "cylinder\n") == 0) {
1870 ibuf[0] = kMainViewGraphicCylinder;
1871 } else if (strcmp(buf, "cone\n") == 0) {
1872 ibuf[0] = kMainViewGraphicCone;
1873 } else if (strcmp(buf, "ellipsoid\n") == 0) {
1874 ibuf[0] = kMainViewGraphicEllipsoid;
1876 continue; /* Skip */
1878 gp = (MainViewGraphic *)calloc(sizeof(MainViewGraphic), 1);
1881 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1887 if (sscanf(buf, "%d %d", &ibuf[0], &ibuf[1]) < 2) {
1888 s_append_asprintf(errbuf, "line %d: the closed/visible flags cannot be read for graphic object", lineNumber);
1891 gp->closed = ibuf[0];
1892 gp->visible = ibuf[1];
1893 } else if (i == 1) {
1894 if (sscanf(buf, "%lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]) < 4) {
1895 s_append_asprintf(errbuf, "line %d: the color cannot be read for graphic object", lineNumber);
1898 for (j = 0; j < 4; j++)
1899 gp->rgba[j] = dbuf[j];
1900 } else if (i == 2) {
1903 s_append_asprintf(errbuf, "line %d: the number of control points must be non-negative", lineNumber);
1907 NewArray(&gp->points, &gp->npoints, sizeof(GLfloat) * 3, j);
1908 } else if (i >= 3 && i < gp->npoints + 3) {
1909 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1910 s_append_asprintf(errbuf, "line %d: the control point cannot be read for graphic object", lineNumber);
1914 gp->points[j++] = dbuf[0];
1915 gp->points[j++] = dbuf[1];
1916 gp->points[j] = dbuf[2];
1917 } else if (i == gp->npoints + 3) {
1920 s_append_asprintf(errbuf, "line %d: the number of normals must be non-negative", lineNumber);
1924 NewArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, j);
1925 } else if (i >= gp->npoints + 4 && i < gp->npoints + gp->nnormals + 4) {
1926 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1927 s_append_asprintf(errbuf, "line %d: the normal vector cannot be read for graphic object", lineNumber);
1930 j = (i - gp->npoints - 4) * 3;
1931 gp->normals[j++] = dbuf[0];
1932 gp->normals[j++] = dbuf[1];
1933 gp->normals[j] = dbuf[2];
1937 MainView_insertGraphic(mp->mview, -1, gp);
1939 if (buf[0] == '\n' || buf[0] == 0)
1944 } else if (strcmp(buf, "!:nbo") == 0) {
1946 char *stringBuf, *returnString;
1947 i = strlen(buf) + 1; /* Including \n */
1948 NewArray(&stringBuf, &stringLen, sizeof(char), i + 1);
1949 strcpy(stringBuf, buf);
1950 strcat(stringBuf, "\n");
1952 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1953 /* The comment lines are _not_ skipped */
1957 AssignArray(&stringBuf, &stringLen, sizeof(char), i + j, NULL);
1958 strncpy(stringBuf + i, buf, j);
1961 if (MolActionCreateAndPerform(mp, SCRIPT_ACTION("si;s"),
1962 "proc { |s,i| mbsfstring2nbo(s,i) }",
1963 stringBuf, k, &returnString) != 0) {
1964 s_append_asprintf(errbuf, "line %d: cannot call mbsfstring2nbo at line ", lineNumber);
1966 } else if (returnString[0] != 0) {
1967 s_append_asprintf(errbuf, "%s", returnString);
1972 } else if (strncmp(buf, "!:@", 3) == 0) {
1973 /* Plug-in implemented in the ruby world */
1975 char *stringBuf, *returnString;
1977 NewArray(&stringBuf, &stringLen, sizeof(char), i + 1);
1978 strcpy(stringBuf, buf);
1980 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1981 /* The comment lines are _not_ skipped */
1985 AssignArray(&stringBuf, &stringLen, sizeof(char), i + j, NULL);
1986 strncpy(stringBuf + i, buf, j);
1989 if (MolActionCreateAndPerform(mp, SCRIPT_ACTION("si;s"),
1990 "proc { |i| loadmbsf_plugin(i) rescue \"line #{i}: #{$i.to_s}\" }",
1991 stringBuf, k, &returnString) != 0) {
1992 s_append_asprintf(errbuf, "line %d: cannot invoke Ruby plugin", lineNumber);
1994 } else if (returnString[0] != 0) {
1995 s_append_asprintf(errbuf, "%s", returnString);
2001 /* Unknown sections are silently ignored */
2005 MoleculeCleanUpResidueTable(mp);
2006 if (mp->arena != NULL)
2007 md_arena_set_molecule(mp->arena, mp);
2011 /* if (mp->mview != NULL) {
2012 if (mview_ibuf[0] != kUndefined)
2013 mp->mview->showUnitCell = mview_ibuf[0];
2014 if (mview_ibuf[1] != kUndefined)
2015 mp->mview->showPeriodicBox = mview_ibuf[1];
2016 if (mview_ibuf[2] != kUndefined)
2017 mp->mview->showExpandedAtoms = mview_ibuf[2];
2018 if (mview_ibuf[3] != kUndefined)
2019 mp->mview->showEllipsoids = mview_ibuf[3];
2020 if (mview_ibuf[4] != kUndefined)
2021 mp->mview->showHydrogens = mview_ibuf[4];
2022 if (mview_ibuf[5] != kUndefined)
2023 mp->mview->showDummyAtoms = mview_ibuf[5];
2024 if (mview_ibuf[6] != kUndefined)
2025 mp->mview->showRotationCenter = mview_ibuf[6];
2026 if (mview_ibuf[7] != kUndefined)
2027 mp->mview->showGraphiteFlag = mview_ibuf[7];
2028 if (mview_ibuf[8] != kUndefined)
2029 mp->mview->showPeriodicImageFlag = mview_ibuf[8];
2030 if (mview_ibuf[9] != kUndefined)
2031 mp->mview->showGraphite = mview_ibuf[9];
2032 if (mview_ibuf[10] != kUndefined && mview_ibuf[10] >= 6)
2033 mp->mview->atomResolution = mview_ibuf[10];
2034 if (mview_ibuf[11] != kUndefined && mview_ibuf[11] >= 4)
2035 mp->mview->bondResolution = mview_ibuf[11];
2036 for (i = 0; i < 6; i++) {
2037 if (mview_ibuf[12 + i] != kUndefined)
2038 mp->mview->showPeriodicImage[i] = mview_ibuf[12 + i];
2040 if (mview_dbuf[8] != kUndefined)
2041 mp->mview->atomRadius = mview_dbuf[8];
2042 if (mview_dbuf[9] != kUndefined)
2043 mp->mview->bondRadius = mview_dbuf[9];
2044 if (mp->mview->track != NULL) {
2045 if (mview_dbuf[0] != kUndefined)
2046 TrackballSetScale(mp->mview->track, mview_dbuf[0]);
2047 if (mview_dbuf[1] != kUndefined)
2048 TrackballSetTranslate(mp->mview->track, mview_dbuf + 1);
2049 if (mview_dbuf[4] != kUndefined)
2050 TrackballSetRotate(mp->mview->track, mview_dbuf + 4);
2059 /* The content of mp may be broken, so make it empty */
2065 MoleculeLoadPsfFile(Molecule *mp, const char *fname, char **errbuf)
2074 Vector *frames = NULL;
2080 else MoleculeClear(mp);
2081 fp = fopen(fname, "rb");
2083 s_append_asprintf(errbuf, "Cannot open file");
2086 /* flockfile(fp); */
2089 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2090 if (strncmp(buf, "PSF", 3) == 0) {
2094 for (p = buf; *p != 0 && isspace(*p); p++) {}
2100 if (strstr(buf, "!COORD") != NULL) {
2101 /* Extended psf file with coordinates */
2103 /* Allocate a temporary storage for frames */
2104 size_t size = sizeof(Vector) * mp->natoms * fn;
2106 frames = (Vector *)malloc(size);
2108 frames = (Vector *)realloc(frames, size);
2112 /* Read coordinates */
2113 for (i = 0; i < mp->natoms; i++) {
2116 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2118 s_append_asprintf(errbuf, "line %d: premature end of file while reading coordinates (frame %d)", lineNumber, fn);
2121 if (sscanf(buf, "%lg %lg %lg", dval, dval + 1, dval + 2) != 3) {
2123 s_append_asprintf(errbuf, "line %d: coordinates cannot be read for atom %d", lineNumber, i + 1);
2130 ATOM_AT_INDEX(mp->atoms, i)->r = r;
2132 frames[mp->natoms * (fn - 1) + i] = r;
2141 ReadFormat(buf, "I8", &natoms);
2144 if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
2147 for (i = 0; i < natoms; i++) {
2149 char segName[5], resName[4], atomName[5], atomType[3], element[3];
2152 memset(&w, 0, sizeof(w));
2153 ap = ATOM_AT_INDEX(mp->atoms, i);
2154 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2156 s_append_asprintf(errbuf, "line %d: premature end of file while reading atoms", lineNumber);
2159 ReadFormat(buf, "I8 x1 S4 I5 x1 S3 x2 S4 x1 S4 F16 F10",
2160 &w.serial, w.segName, &ap->resSeq, w.resName, w.atomName,
2161 w.atomType, &ap->charge, &ap->weight);
2162 strncpy(ap->segName, w.segName, 4);
2163 strncpy(ap->resName, w.resName, 3);
2164 strncpy(ap->aname, w.atomName, 4);
2165 ap->type = AtomTypeEncodeToUInt(w.atomType);
2166 /* $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl */
2167 ap->atomicNumber = GuessAtomicNumber(w.atomName, ap->weight);
2168 ElementToString(ap->atomicNumber, w.element);
2169 strncpy(ap->element, w.element, 2);
2170 /* w.element[0] = 0;
2171 for (p = w.atomName; *p != 0; p++) {
2172 if (isalpha(*p) && *p != '_') {
2173 w.element[0] = toupper(*p);
2174 if (isalpha(p[1]) && p[1] != '_') {
2175 w.element[1] = toupper(p[1]);
2183 strncpy(ap->element, w.element, 2);
2184 ap->atomicNumber = ElementToInt(w.element); */
2185 if (w.resName[0] == 0)
2186 strncpy(ap->resName, "XXX", 3);
2187 if (ap->resSeq > mp->nresidues)
2188 mp->nresidues = ap->resSeq;
2190 if (mp->residues != NULL)
2192 if (NewArray(&mp->residues, &mp->nresidues, sizeof(char (*)[4]), mp->nresidues + 1) == 0)
2194 for (i = 0; i < mp->natoms; i++) {
2195 j = mp->atoms[i].resSeq;
2196 if (mp->residues[j][0] == 0)
2197 strncpy(mp->residues[j], mp->atoms[i].resName, 4);
2200 } else if (section == 3) {
2204 ReadFormat(buf, "I8", &nbonds);
2207 if (NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, nbonds) == NULL)
2210 for (i = 0; i < nbonds; i += 4) {
2211 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2212 s_append_asprintf(errbuf, "line %d: premature end of file while reading bonds", lineNumber);
2216 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
2217 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
2218 for (j = 0; j < 4 && i + j < nbonds; j++) {
2221 b1 = ibuf[j * 2] - 1; /* Internal atom number is 0-based */
2222 b2 = ibuf[j * 2 + 1] - 1;
2223 if (b1 < 0 || b1 >= mp->natoms || b2 < 0 || b2 >= mp->natoms) {
2224 s_append_asprintf(errbuf, "line %d: The bond %d-%d includes non-existent atom", lineNumber, b1+1, b2+1);
2230 ap = ATOM_AT_INDEX(mp->atoms, b1);
2231 AtomConnectInsertEntry(&ap->connect, -1, b2);
2232 ap = ATOM_AT_INDEX(mp->atoms, b2);
2233 AtomConnectInsertEntry(&ap->connect, -1, b1);
2237 } else if (section == 4) {
2241 ReadFormat(buf, "I8", &nangles);
2244 if (NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, nangles) == NULL)
2247 for (i = 0; i < nangles; i += 3) {
2248 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2249 s_append_asprintf(errbuf, "line %d: premature end of file while reading angles", lineNumber);
2253 ReadFormat(buf, "I8I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
2254 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7, ibuf + 8);
2255 for (j = 0; j < 3 && i + j < nangles; j++) {
2257 a1 = ibuf[j * 3] - 1; /* Internal atom number is 0-based */
2258 a2 = ibuf[j * 3 + 1] - 1;
2259 a3 = ibuf[j * 3 + 2] - 1;
2260 if (a1 < 0 || a1 >= mp->natoms || a2 < 0 || a2 >= mp->natoms || a3 < 0 || a3 >= mp->natoms) {
2261 s_append_asprintf(errbuf, "line %d: The angle %d-%d-%d includes non-existent atom", lineNumber, a1+1, a2+1, a3+1);
2271 } else if (section == 5 || section == 6) {
2272 /* Dihedrals and Impropers */
2275 ReadFormat(buf, "I8", &ndihedrals);
2276 if (ndihedrals == 0)
2279 if (NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, ndihedrals) == NULL)
2283 if (NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, ndihedrals) == NULL)
2287 for (i = 0; i < ndihedrals; i += 2) {
2288 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2290 s_append_asprintf(errbuf, "line %d: premature end of file while reading %s", lineNumber, (section == 5 ? "dihedral" : "improper"));
2294 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3, ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
2295 for (j = 0; j < 2 && i + j < ndihedrals; j++) {
2297 d1 = ibuf[j * 4] - 1; /* Internal atom number is 0-based */
2298 d2 = ibuf[j * 4 + 1] - 1;
2299 d3 = ibuf[j * 4 + 2] - 1;
2300 d4 = ibuf[j * 4 + 3] - 1;
2301 if (d1 < 0 || d1 >= mp->natoms || d2 < 0 || d2 >= mp->natoms || d3 < 0 || d3 >= mp->natoms || d4 < 0 || d4 >= mp->natoms) {
2302 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);
2316 /* Create frames for each atom if necessary */
2318 for (i = 0; i < mp->natoms; i++) {
2319 ap = ATOM_AT_INDEX(mp->atoms, i);
2320 NewArray(&ap->frames, &ap->nframes, sizeof(Vector), fn);
2321 if (ap->frames == NULL)
2323 for (j = 0; j < fn; j++)
2324 ap->frames[j] = frames[mp->natoms * j + i];
2331 /* funlockfile(fp); */
2333 mp->nframes = -1; /* Should be recalculated later */
2336 else if (section == -1)
2340 Panic("low memory while reading structure file %s", fname);
2341 return 1; /* not reached */
2344 /* ("-x", "y", "-z+0.5") -> (-1,0,0,0,0,1,0,0,0,0,-1,0.5) */
2346 sMoleculeSymopStringsToTransform(char **symops, Transform tr)
2350 memset(tr, 0, sizeof(Transform));
2351 for (i = 0; i < 3; i++) {
2355 while (*symop != 0) {
2357 while (isspace(*symop))
2359 if (*symop == 0 || *symop == '\r' || *symop == 'n')
2361 if (*symop == '-') {
2364 } else if (*symop == '+') {
2368 while (isspace(*symop))
2370 if (*symop == '.' || isdigit(*symop)) {
2371 /* Numerical offset */
2372 double d = strtod(symop, &symop);
2373 if (*symop == '/') {
2374 double dd = strtod(symop + 1, &symop);
2378 return 1; /* Bad format */
2381 } else if (*symop == 'x' || *symop == 'X') {
2384 } else if (*symop == 'y' || *symop == 'Y') {
2387 } else if (*symop == 'z' || *symop == 'Z') {
2390 } else return 1; /* Bad format */
2391 } /* end while (*symop != 0) */
2397 sMoleculeGenerateSymopWithTransform(Molecule *mp, Transform gtr, int num)
2403 for (i = 0; i < num; i++) {
2404 memmove(tr, mp->syms[i], sizeof(Transform));
2405 TransformMul(tr, gtr, tr);
2406 for (j = 9; j < 12; j++) {
2409 else if (tr[j] <= 0.0)
2412 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
2419 char *p = buf + strlen(buf) - 1;
2420 if (p >= buf && (*p == '\n' || *p == '\r')) {
2422 if (--p >= buf && (*p == '\n' || *p == '\r')) {
2430 MoleculeLoadTepFile(Molecule *mp, const char *fname, char **errbuf)
2443 fp = fopen(fname, "rb");
2445 s_append_asprintf(errbuf, "Cannot open file");
2449 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2450 if (section == -1) {
2457 ReadFormat(buf, "I1F8F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5);
2459 MoleculeSetCell(mp, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], 0);
2466 if (cellType == 0) {
2467 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);
2481 char *symops[3], *brks;
2483 memset(tr, 0, sizeof(Transform));
2484 ReadFormat(buf, "I1", ibuf);
2485 symops[0] = strtok_r(buf + 1, ", ", &brks);
2486 symops[1] = strtok_r(NULL, ", ", &brks);
2487 symops[2] = strtok_r(NULL, ", ", &brks);
2488 if (sMoleculeSymopStringsToTransform(symops, tr)) {
2489 s_append_asprintf(errbuf, "line %d: bad symmetry specification", lineNumber);
2493 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2499 if (section == 2) { /* Atoms */
2503 int atomIndex = mp->natoms;
2504 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2505 memset(ap, 0, gSizeOfAtomRecord);
2506 ReadFormat(buf, "S6x3x9x9F9F9F9F9", name, fbuf, fbuf+1, fbuf+2, fbuf+3);
2507 strncpy(ap->aname, name, 4);
2511 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2512 /* ap->atomicNumber = AtomNameToElement(ap->name);
2513 ElementToString(ap->atomicNumber, ap->element); */
2514 /* sAtomSetElement(ap, -1, ap->name); */
2517 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2518 s_append_asprintf(errbuf, "unexpected end of file");
2521 ReadFormat(buf, "I1F8F9F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2523 if ((atomType >= 0 && atomType <= 5) || (atomType >= 8 && atomType <= 10)) {
2524 /* Anisotropic thermal parameters */
2525 MoleculeSetAniso(mp, atomIndex, atomType, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[5], fbuf[4], NULL);
2533 MoleculeGuessBonds(mp, 0.0, &nbonds, &bonds);
2535 MoleculeAddBonds(mp, nbonds, bonds, NULL, 1);
2538 mp->nframes = -1; /* Should be recalculated later */
2541 Panic("low memory while reading structure file %s", fname);
2542 return -1; /* not reached */
2546 MoleculeLoadShelxFile(Molecule *mp, const char *fname, char **errbuf)
2554 int currentResSeq = 0;
2555 char currentResName[6];
2562 char (*sfacs)[4] = NULL;
2567 currentResName[0] = 0;
2568 fp = fopen(fname, "rb");
2570 s_append_asprintf(errbuf, "Cannot open file");
2574 tr[0] = tr[4] = tr[8] = 1;
2575 tr[1] = tr[2] = tr[3] = tr[5] = tr[6] = tr[7] = tr[9] = tr[10] = tr[11] = 0;
2576 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), 0, tr) == 0)
2578 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2579 if (strncmp(buf, "CELL", 4) == 0) {
2581 sscanf(buf + 4, " %f %f %f %f %f %f %f", fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2582 MoleculeSetCell(mp, fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], fbuf[6], 0);
2584 } else if (strncmp(buf, "SFAC", 4) == 0) {
2586 for (p1 = strtok_r(buf + 4, " ", &p2); p1 != NULL; p1 = strtok_r(NULL, " ", &p2)) {
2587 char *pp = (char *)AssignArray(&sfacs, &nsfacs, 4, nsfacs, NULL);
2594 } else if (strncmp(buf, "LATT", 4) == 0) {
2595 sscanf(buf + 4, " %d", &latticeType);
2597 } else if (strncmp(buf, "SYMM", 4) == 0) {
2598 char *symops[3], *brks;
2599 memset(tr, 0, sizeof(Transform));
2600 // ReadFormat(buf + 4, "I1", ibuf);
2602 symops[0] = strtok_r(buf + 4, ",", &brks);
2603 symops[1] = strtok_r(NULL, ",", &brks);
2604 symops[2] = strtok_r(NULL, ",", &brks);
2605 if (sMoleculeSymopStringsToTransform(symops, tr)) {
2606 s_append_asprintf(errbuf, "line %d: bad symmetry specification", lineNumber);
2609 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2612 } else if (strncmp(buf, "RESI", 4) == 0) {
2613 for (p1 = buf + 4; isspace(*p1); p1++);
2615 for (p2 = p1 + 1; isalnum(*p2); p2++);
2617 strncpy(currentResName, p1, 4);
2618 currentResName[4] = 0;
2620 } else currentResName[0] = 0;
2621 sscanf(buf + 4, " %d", ¤tResSeq);
2624 /* Atom name: [A-Za-z]{1,2}[0-9]* */
2625 for (p1 = buf; p1 < buf + 2 && (isalpha(*p1) && *p1 != '_'); p1++);
2627 while (isdigit(*p1))
2630 if (p1 > buf && p1 <= buf + 4 && isspace(*p1)) {
2634 int atomIndex = mp->natoms;
2635 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2636 memset(ap, 0, gSizeOfAtomRecord);
2637 strncpy(ap->aname, buf, 4);
2638 n = sscanf(p1, " %d %f %f %f %f %f %f %2s", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, cont);
2639 if (n == 8 && strcmp(cont, "=") == 0) {
2640 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2641 s_append_asprintf(errbuf, "line %d: unexpected end of file within the atom cards", lineNumber);
2644 sscanf(buf, " %f %f %f %f", fbuf+6, fbuf+7, fbuf+8, fbuf+9);
2646 } else n = 5; /* Iso */
2650 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2651 ap->occupancy = fbuf[3];
2652 if (ap->aname[0] != 'Q' && ibuf[0] >= 1 && ibuf[0] <= nsfacs) {
2653 strncpy(ap->element, sfacs[ibuf[0] - 1], 2);
2655 /* sAtomSetElement(ap, -1, sfacs[ibuf[0] - 1]); */
2656 /* strncpy(ap->element, sfacs[ibuf[0] - 1], 4);
2657 ap->atomicNumber = ElementToInt(ap->element); */
2659 sAtomSetElement(ap, -1, ap->name); */
2660 /* ap->atomicNumber = AtomNameToElement(ap->name);
2661 ElementToString(ap->atomicNumber, ap->element); */
2664 if (n == 10 || fbuf[4] >= 0.0) {
2666 /* Read in the standard deviations */
2667 ReadLine(buf, sizeof buf, fp, &lineNumber);
2668 for (i = 0; i < 9; i++) {
2672 dbuf[i] = strtod(buf + j, NULL);
2675 ap->sigma.x = dbuf[0];
2676 ap->sigma.y = dbuf[1];
2677 ap->sigma.z = dbuf[2];
2680 ap->tempFactor = fbuf[4] * 78.9568352087147; /* 8*pi*pi */
2682 MoleculeSetAniso(mp, atomIndex, 8, fbuf[4], fbuf[5], fbuf[6], fbuf[9], fbuf[7], fbuf[8], dbuf);
2683 ap->resSeq = currentResSeq;
2684 strncpy(ap->resName, currentResName, 4);
2691 /* Add symmetry operations according to the lattice type */
2692 switch (latticeType < 0 ? -latticeType : latticeType) {
2693 static Transform tr_i = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0.5};
2694 static Transform tr_c = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0};
2695 static Transform tr_a = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.5};
2696 static Transform tr_b = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.5};
2697 static Transform tr_r1 = {0, 1, 0, -1, -1, 0, 0, 0, 1, 0, 0, 0};
2698 static Transform tr_r2 = {-1, -1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0};
2702 sMoleculeGenerateSymopWithTransform(mp, tr_i, 0);
2704 case 3: /* Rhombohedral obverse on hexagonal axes */
2706 sMoleculeGenerateSymopWithTransform(mp, tr_r1, n);
2707 sMoleculeGenerateSymopWithTransform(mp, tr_r2, n);
2711 sMoleculeGenerateSymopWithTransform(mp, tr_a, n);
2712 sMoleculeGenerateSymopWithTransform(mp, tr_b, n);
2713 sMoleculeGenerateSymopWithTransform(mp, tr_c, n);
2716 sMoleculeGenerateSymopWithTransform(mp, tr_a, 0);
2719 sMoleculeGenerateSymopWithTransform(mp, tr_b, 0);
2722 sMoleculeGenerateSymopWithTransform(mp, tr_c, 0);
2726 if (latticeType > 0) {
2727 static Transform tr_inv = {-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0};
2728 sMoleculeGenerateSymopWithTransform(mp, tr_inv, 0);
2731 MoleculeGuessBonds(mp, 0.0, &nbonds, &bonds);
2733 MoleculeAddBonds(mp, nbonds, bonds, NULL, 1);
2736 mp->nframes = -1; /* Should be recalculated later */
2739 Panic("low memory while reading structure file %s", fname);
2740 return -1; /* not reached */
2743 /* Add one gaussian orbital shell information (not undoable) */
2745 MoleculeAddGaussianOrbitalShell(Molecule *mol, Int a_idx, Int sym, Int nprims, Int add_exp)
2750 return -1; /* Molecule is empty */
2753 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2755 return -2; /* Low memory */
2757 shellp = AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), bset->nshells, NULL);
2759 return -2; /* Low memory */
2761 case 0: shellp->sym = kGTOType_S; shellp->ncomp = 1; break;
2762 case 1: shellp->sym = kGTOType_P; shellp->ncomp = 3; break;
2763 case -1: shellp->sym = kGTOType_SP; shellp->ncomp = 4; break;
2764 case 2: shellp->sym = kGTOType_D; shellp->ncomp = 6; break;
2765 case -2: shellp->sym = kGTOType_D5; shellp->ncomp = 5; break;
2766 case 3: shellp->sym = kGTOType_F; shellp->ncomp = 10; break;
2767 case -3: shellp->sym = kGTOType_F7; shellp->ncomp = 7; break;
2768 case 4: shellp->sym = kGTOType_G; shellp->ncomp = 15; break;
2769 case -4: shellp->sym = kGTOType_G9; shellp->ncomp = 9; break;
2771 return -3; /* Unsupported shell type */
2773 shellp->nprim = nprims;
2774 shellp->a_idx = a_idx;
2775 if (bset->shells < shellp) {
2776 shellp->m_idx = shellp[-1].m_idx + shellp[-1].ncomp;
2777 shellp->p_idx = shellp[-1].p_idx + shellp[-1].nprim;
2782 shellp->add_exp = add_exp;
2783 /* Update the number of components (if not yet determined) */
2784 if (bset->ncomps < shellp->m_idx + shellp->ncomp)
2785 bset->ncomps = shellp->m_idx + shellp->ncomp;
2789 /* Add a set of gaussian primitive coefficients (not undoable) */
2791 MoleculeAddGaussianPrimitiveCoefficients(Molecule *mol, Double exponent, Double contraction, Double contraction_sp)
2796 return -1; /* Molecule is empty */
2799 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2801 return -2; /* Low memory */
2803 primp = AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), bset->npriminfos, NULL);
2805 return -2; /* Low memory */
2806 primp->A = exponent;
2807 primp->C = contraction;
2808 primp->Csp = contraction_sp;
2812 /* Get the shell information from the component index */
2813 /* The outLabel must have space for at least 31 non-Null characters */
2814 /* See also: sub load_janpa_log in loadsave.rb */
2816 MoleculeGetGaussianComponentInfo(Molecule *mol, Int comp_idx, Int *outAtomIdx, char outLabel[32], Int *outShellIdx)
2821 if (mol == NULL || (bset = mol->bset) == NULL)
2822 return -1; /* No basis set info */
2823 if (comp_idx < 0 || comp_idx >= bset->ncomps)
2824 return -2; /* Component index out of range */
2825 for (si = 0, shellp = bset->shells; si < bset->nshells; si++, shellp++) {
2826 if (comp_idx >= shellp->ncomp) {
2827 comp_idx -= shellp->ncomp;
2830 static const char *type_p = "xyz";
2831 static const char *type_d = "xxyyzzxyxzyz";
2832 static const char *type_d5[] = {"zz-rr", "xy", "yz", "xx-yy", "xy"};
2833 static const char *type_f = "xxxyyyzzzxxyxxzxyyyyzxzzyzzxyz";
2834 static const char *type_f7[] = {"z3-zr2", "xz2-xr2", "yz2-yr2", "x2z-y2z", "xyz", "x3-xy2", "x2y-y3"};
2835 static const char *type_g[] = {"x4", "y4", "z4", "x3y", "x3z", "xy3", "y3z", "xz3", "yz3", "x2y2", "x2z2", "y2z2", "x2yz", "xy2z", "xyz2"};
2836 static const char *type_g9[] = {"z4-z2r2+r4", "xz3-xzr2", "yz3-yzr2", "x2z2-y2z2", "xyz2-xyr2", "x3z-xy2z", "x2yz-y3z", "x4-x2y2+y4", "x3y-xy3"};
2837 *outAtomIdx = shellp->a_idx;
2839 switch (shellp->sym) {
2841 strcpy(outLabel, "S");
2845 outLabel[1] = type_p[comp_idx];
2850 strcpy(outLabel, "S");
2853 outLabel[1] = type_p[comp_idx - 1];
2859 strncpy(outLabel + 1, type_d + comp_idx * 2, 2);
2864 strncpy(outLabel + 1, type_d5[comp_idx], 30);
2869 strncpy(outLabel + 1, type_f + comp_idx * 3, 3);
2874 strncpy(outLabel + 1, type_f7[comp_idx], 30);
2879 strncpy(outLabel + 1, type_g[comp_idx], 30);
2884 strncpy(outLabel + 1, type_g9[comp_idx], 30);
2888 return -3; /* Unsupported orbital type (internal error) */
2890 if (shellp->add_exp > 0) {
2891 /* Additional exponent (JANPA extension): like "s*r2", "dxx-yy*r4", etc. */
2892 int len = strlen(outLabel);
2894 snprintf(outLabel + len, 32 - len, "*r%d", shellp->add_exp);
2901 return -4; /* comp_idx out of range? (internal error) */
2904 /* Set MO coefficients for idx-th MO (1-based) */
2906 MoleculeSetMOCoefficients(Molecule *mol, Int idx, Double energy, Int ncomps, Double *coeffs)
2911 return -1; /* Molecule is empty */
2914 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2916 return -2; /* Low memory */
2918 if (bset->nmos == 0) {
2919 if (bset->nshells > 0) {
2920 /* Shell info is already set: calculate the number of MOs from there */
2921 for (i = n = 0; i < bset->nshells; i++)
2922 n += bset->shells[i].ncomp;
2924 } else if (ncomps > 0) {
2925 bset->ncomps = ncomps;
2927 if (bset->rflag == 0)
2928 bset->nmos = bset->ncomps * 2;
2930 bset->nmos = bset->ncomps;
2931 if (bset->nmos <= 0)
2932 return -3; /* Bad or inconsistent number of MOs */
2933 bset->mo = (Double *)calloc(sizeof(Double), (bset->nmos + 1) * bset->ncomps);
2934 bset->moenergies = (Double *)calloc(sizeof(Double), bset->nmos + 1);
2935 if (bset->mo == NULL || bset->moenergies == NULL) {
2936 if (bset->mo != NULL)
2938 if (bset->moenergies != NULL)
2939 free(bset->moenergies);
2941 bset->moenergies = NULL;
2943 return -2; /* Low memory */
2947 idx = -idx + bset->ncomps;
2948 if (idx < 0 || idx > bset->nmos)
2949 return -4; /* Bad MO index */
2951 idx = bset->nmos; /* Arbitrary vector */
2954 if (energy != -1000000)
2955 bset->moenergies[idx] = energy;
2956 if (ncomps < bset->ncomps)
2957 return -5; /* Insufficient number of data provided */
2958 memmove(bset->mo + (idx * bset->ncomps), coeffs, sizeof(Double) * bset->ncomps);
2959 if (bset->cns != NULL) {
2960 /* Clear the cached values */
2968 /* Get MO coefficients for idx-th MO (1-based) */
2969 /* Caution: *ncoeffs and *coeffs should be valid _before_ calling this function, i.e. */
2970 /* *ncoeffs = 0 && *coeffs = NULL or *coeffs is a valid memory pointer and *ncoeffs */
2971 /* properly designates the memory size as an array of Doubles. */
2973 MoleculeGetMOCoefficients(Molecule *mol, Int idx, Double *energy, Int *ncoeffs, Double **coeffs)
2977 return -1; /* Molecule is empty */
2979 if (bset == NULL || bset->ncomps <= 0)
2980 return -2; /* No basis set info */
2982 idx = -idx + bset->ncomps;
2983 if (idx < 0 || idx > bset->nmos)
2984 return -3; /* MO index out of range */
2986 idx = bset->nmos; /* Arbitrary vector */
2990 *energy = bset->moenergies[idx];
2991 if (ncoeffs != NULL && coeffs != NULL) {
2992 if (*ncoeffs < bset->ncomps || *coeffs == NULL) {
2993 if (*coeffs != NULL)
2994 free(*coeffs); /* Caution: possible cause of SIGBUS if *coeff is not initialized properly */
2995 *coeffs = (Double *)calloc(sizeof(Double), bset->ncomps);
2996 *ncoeffs = bset->ncomps;
2998 memmove(*coeffs, bset->mo + (idx * bset->ncomps), sizeof(Double) * bset->ncomps);
3003 /* Set Basic MO Info. rflag: 0, UHF; 1, RHF; 2, ROHF; -1, clear
3004 ne_alpha: number of alpha electrons, ne_beta: number of beta electrons */
3006 MoleculeSetMOInfo(Molecule *mol, Int rflag, Int ne_alpha, Int ne_beta)
3009 if (mol == NULL || mol->natoms == 0)
3010 return -1; /* Molecule is empty */
3012 if (mol->bset != NULL) {
3013 BasisSetRelease(mol->bset);
3020 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
3022 return -2; /* Low memory */
3024 bset->natoms_bs = mol->natoms;
3025 bset->ne_alpha = ne_alpha;
3026 bset->ne_beta = ne_beta;
3027 bset->rflag = rflag;
3032 sSeparateTokens(char *inString, char **outPtr, int size)
3036 for (i = 0; i < size; i++) {
3037 p = strtok((i == 0 ? inString : NULL), " \r\n");
3048 sReadNumberArray(void *basep, Int *countp, Int size, Int num, FILE *fp, int *lnp)
3052 *((void **)basep) = NULL;
3054 if (AssignArray(basep, countp, size, num - 1, NULL) == NULL)
3055 return 4; /* Out of memory */
3057 while (ReadLine(buf, sizeof buf, fp, lnp) > 0) {
3058 char *tokens[16], *p;
3059 sSeparateTokens(buf, tokens, 16);
3060 for (i = 0; i < 16; i++) {
3061 if (tokens[i] == NULL)
3063 if (size == sizeof(Int)) {
3064 (*((Int **)basep))[n] = strtol(tokens[i], &p, 0);
3065 } else if (size == sizeof(Double)) {
3066 (*((Double **)basep))[n] = strtod(tokens[i], &p);
3067 } else return -1; /* Internal error */
3068 if (tokens[i] == p || *p != 0)
3069 return 1; /* Non-digit character */
3071 if (i < 15 && tokens[i + 1] != NULL)
3072 return 2; /* Too many data */
3073 return 0; /* All data are successfully read */
3077 return 3; /* Unexpected EOF */
3080 // Normalization constant for one gaussian component
3081 // 1/sqrt(Integral((Y(lm)*(r^n)*exp(-a*r*r))^2, for all r = (x, y, z)))
3082 // where Y(lm) is a spherical harmonic function, r^n is an "additional exponent"
3083 // required in expanded Molden file generated by JANPA, and a is the exponent
3084 // of the gaussian component.
3085 // The function Y(lm) is assumed so that its norm equals sqrt(4*pi/(2l+1))
3086 // for each m in [-l..l].
3088 sGaussianNormalizationConstant(int l, double a, int n)
3090 return 1.0/(sqrt(4 * PI / (2 * l + 1.0)) * sqrt(tgamma(l + n + 1.5) / (2.0 * pow(2.0 * a, l + n + 1.5))));
3094 sSetupGaussianCoefficients(BasisSet *bset)
3101 /* Cache the contraction coefficients for efficient calculation */
3102 /* Sum up the number of components for all primitives */
3103 for (i = k = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3105 k += sp->nprim * sp->ncomp;
3107 /* Allocate memory for the cached values */
3108 if (AssignArray(&bset->cns, &bset->ncns, sizeof(Double), k - 1, NULL) == NULL)
3110 /* Iterate over all primitives */
3112 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3113 for (j = 0, pp = bset->priminfos + sp->p_idx; j < sp->nprim; j++, pp++) {
3117 // GNC(0,a,n) * r^n * exp(-a*r^2)
3118 d = pp->C * sGaussianNormalizationConstant(0, pp->A, n);
3120 //{ printf("type_S: %g %g\n", d, pp->C * pow(pp->A, 0.75) * 0.71270547); }
3121 // (8 alpha^3/pi^3)^0.25 exp(-alpha r^2)
3122 //*dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
3125 // GNC(1,a,n) * [x|y|z] * r^n * exp(-a*r^2)
3126 d = pp->C * sGaussianNormalizationConstant(1, pp->A, n);
3127 //{ printf("type_P: %g %g\n", d, pp->C * pow(pp->A, 1.25) * 1.425410941); }
3128 // (128 alpha^5/pi^3)^0.25 [x|y|z]exp(-alpha r^2)
3129 // d = pp->C * pow(pp->A, 1.25) * 1.425410941;
3135 // GNC(0,a,n) * r^n * exp(-a*r^2)
3136 *dp++ = d = pp->C * sGaussianNormalizationConstant(0, pp->A, n);
3137 //{ printf("type_SP(s): %g %g\n", d, pp->C * pow(pp->A, 0.75) * 0.71270547); }
3138 // GNC(1,a,n) * [x|y|z] * r^n * exp(-a*r^2)
3139 d = pp->Csp * sGaussianNormalizationConstant(1, pp->A, n);
3140 //{ printf("type_SP(p): %g %g\n", d, pp->Csp * pow(pp->A, 1.25) * 1.425410941); }
3141 //*dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
3142 //d = pp->Csp * pow(pp->A, 1.25) * 1.425410941;
3148 // GNC(2,a,n) * [xx|yy|zz] * r^n * exp(-a*r^2)
3149 // GNC(2,a,n) * sqrt(3) * [xy|yz|zx] * r^n * exp(-a*r^2)
3150 d = pp->C * sGaussianNormalizationConstant(2, pp->A, n);
3151 //{ printf("type_D[0-2]: %g %g\n", d, pp->C * pow(pp->A, 1.75) * 1.645922781); }
3152 //{ printf("type_D[3-5]: %g %g\n", d * sqrt(3), pp->C * pow(pp->A, 1.75) * 2.850821881); }
3153 dp[0] = dp[1] = dp[2] = d;
3154 dp[3] = dp[4] = dp[5] = d * sqrt(3);
3155 // xx|yy|zz: (2048 alpha^7/9pi^3)^0.25 [xx|yy|zz]exp(-alpha r^2)
3156 // xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
3157 // d = pp->C * pow(pp->A, 1.75);
3158 //dp[0] = dp[1] = dp[2] = d * 1.645922781;
3159 //dp[3] = dp[4] = dp[5] = d * 2.850821881;
3163 // D(0): GNC(2,a,n) * (1/2) * (3zz-rr) * r^n * exp(-a*r^2)
3164 // D(+1): GNC(2,a,n) * sqrt(3) * xz * r^n * exp(-a*r^2)
3165 // D(-1): GNC(2,a,n) * sqrt(3) * yz * r^n * exp(-a*r^2)
3166 // D(+2): GNC(2,a,n) * (sqrt(3)/2) * (xx-yy) * r^n * exp(-a*r^2)
3167 // D(-2): GNC(2,a,n) * sqrt(3) * xy * r^n * exp(-a*r^2)
3168 d = pp->C * sGaussianNormalizationConstant(2, pp->A, n);
3169 //{ printf("type_D5[0]: %g %g\n", d * 0.5, pp->C * pow(pp->A, 1.75) * 0.822961390); }
3170 //{ printf("type_D5[1,2,4]: %g %g\n", d * sqrt(3), pp->C * pow(pp->A, 1.75) * 2.850821881); }
3171 //{ printf("type_D5[3]: %g %g\n", d * sqrt(3) * 0.5, pp->C * pow(pp->A, 1.75) * 1.425410941); }
3173 dp[1] = dp[2] = dp[4] = d * sqrt(3);
3174 dp[3] = d * sqrt(3) * 0.5;
3175 // 3zz-rr: (128 alpha^7/9pi^3)^0.25 (3zz-rr)exp(-alpha r^2)
3176 // xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
3177 // xx-yy: (128 alpha^7/pi^3)^0.25 (xx-yy)exp(-alpha r^2)
3178 //d = pp->C * pow(pp->A, 1.75);
3179 //dp[0] = d * 0.822961390;
3180 //dp[1] = dp[2] = dp[4] = d * 2.850821881;
3181 //dp[3] = d * 1.425410941;
3185 // GNC(3,a,n) * [xxx|yyy|zzz] * r^n * exp(-a*r^2)
3186 // GNC(3,a,n) * sqrt(5) * [xyy|xxy|xxz|xzz|yzz|yyz] * r^n * exp(-a*r^2)
3187 // GNC(3,a,n) * sqrt(15) * xyz * r^n * exp(-a*r^2)
3188 d = pp->C * sGaussianNormalizationConstant(3, pp->A, n);
3189 dp[0] = dp[1] = dp[2] = d;
3190 dp[3] = dp[4] = dp[5] = dp[6] = dp[7] = dp[8] = d * sqrt(5);
3191 dp[9] = d * sqrt(15);
3195 // F(0): GNC(3,a,n) * (1/2) * (5zzz-3zrr) * r^n * exp(-a*r^2)
3196 // F(+1): GNC(3,a,n) * sqrt(3/8) * (5xzz-xrr) * r^n * exp(-a*r^2)
3197 // F(-1): GNC(3,a,n) * sqrt(3/8) * (5yzz-yrr) * r^n * exp(-a*r^2)
3198 // F(+2): GNC(3,a,n) * sqrt(15/4) * (xxz-yyz) * r^n * exp(-a*r^2)
3199 // F(-2): GNC(3,a,n) * sqrt(15) * xyz * r^n * exp(-a*r^2)
3200 // F(+3): GNC(3,a,n) * sqrt(5/8) * (xxx-3xyy) * r^n * exp(-a*r^2)
3201 // F(-3): GNC(3,a,n) * sqrt(5/8) * (3xxy-yyy) * r^n * exp(-a*r^2)
3202 d = pp->C * sGaussianNormalizationConstant(3, pp->A, n);
3204 dp[1] = dp[2] = d * sqrt(3/8.0);
3205 dp[3] = d * sqrt(15/4.0);
3206 dp[4] = d * sqrt(15);
3207 dp[5] = dp[6] = d * sqrt(5/8.0);
3211 // GNC(4,a,n) * [xxxx|yyyy|zzzz] * exp(-a*r^2)
3212 // GNC(4,a,n) * sqrt(7) * [xxxy|xxxz|yyyx|yyyz|zzzx|zzzy] * exp(-a*r^2)
3213 // GNC(4,a,n) * sqrt(35/3) * [xxyy|xxzz|yyzz] * exp(-a*r^2)
3214 // GNC(4,a,n) * sqrt(35) * [xxyz|yyzx|zzxy] * exp(-a*r^2)
3215 d = pp->C * sGaussianNormalizationConstant(4, pp->A, n);
3216 dp[0] = dp[1] = dp[2] = d;
3217 dp[3] = dp[4] = dp[5] = dp[6] = dp[7] = dp[8] = d * sqrt(7);
3218 dp[9] = dp[10] = dp[11] = d * sqrt(35/3.0);
3219 dp[12] = dp[13] = dp[14] = d * sqrt(35);
3223 // G(0): GNC(4,a,n) * (1/8) * (35zzzz-30zzrr+3rrrr) * exp(-a*r^2)
3224 // G(+1): GNC(4,a,n) * sqrt(5/8) * (7xzzz-3xzrr) * exp(-a*r^2)
3225 // G(-1): GNC(4,a,n) * sqrt(5/8) * (7yzzz-3yzrr) * exp(-a*r^2)
3226 // G(+2): GNC(4,a,n) * sqrt(5/16) * (xx-yy)(7zz-rr) * exp(-a*r^2)
3227 // G(-2): GNC(4,a,n) * sqrt(5/4) * (7xyzz-xyrr) * exp(-a*r^2)
3228 // G(+3): GNC(4,a,n) * sqrt(35/8) * (xxxz-3xyyz) * exp(-a*r^2)
3229 // G(-3): GNC(4,a,n) * sqrt(35/8) * (3xxyz-yyyz) * exp(-a*r^2)
3230 // G(+4): GNC(4,a,n) * sqrt(35/64) * (xxxx-6xxyy+yyyy) * exp(-a*r^2)
3231 // G(-4): GNC(4,a,n) * sqrt(35/4) * (xxxy-xyyy) * exp(-a*r^2)
3232 d = pp->C * sGaussianNormalizationConstant(4, pp->A, n);
3234 dp[1] = dp[2] = d * sqrt(5/8.0);
3235 dp[3] = d * sqrt(5/16.0);
3236 dp[4] = d * sqrt(5/4.0);
3237 dp[5] = dp[6] = d * sqrt(35/8.0);
3238 dp[7] = d * sqrt(35/64.0);
3239 dp[8] = d * sqrt(35/4.0);
3249 MoleculeLoadGaussianFchkFile(Molecule *mp, const char *fname, char **errbuf)
3254 int natoms, nbasis, i, j, k, n, mxbond, retval, ncomps, nprims, nelec;
3268 bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
3272 fp = fopen(fname, "rb");
3274 s_append_asprintf(errbuf, "Cannot open file");
3278 natoms = nbasis = -1;
3286 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3289 if (lineNumber == 2) {
3292 bset->rflag = 0; /* UHF */
3293 else if (buf[11] == 'O')
3294 bset->rflag = 2; /* ROHF */
3295 else bset->rflag = 1; /* RHF */
3298 while (p > buf && *p == ' ')
3301 sSeparateTokens(buf + 42, tokens, 16);
3302 if (strcmp(buf, "Number of atoms") == 0) {
3303 if (tokens[1] == NULL || (natoms = atoi(tokens[1])) <= 0) {
3304 s_append_asprintf(errbuf, "Line %d: strange number of atoms: %s", lineNumber, tokens[1]);
3308 bset->natoms_bs = natoms;
3309 /* Allocate atom records (all are empty for now) */
3310 AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms - 1, NULL);
3311 /* Also allocate atom position array for MO calculations */
3312 /* AssignArray(&bset->pos, &bset->natoms, sizeof(Vector), natoms - 1, NULL); */
3313 /* Also allocate nuclear charge array */
3314 bset->nuccharges = (Double *)calloc(sizeof(Double), natoms);
3315 } else if (strcmp(buf, "Number of electrons") == 0) {
3316 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
3317 s_append_asprintf(errbuf, "Line %d: strange number of electrons: %s", lineNumber, tokens[1]);
3322 } else if (strcmp(buf, "Number of alpha electrons") == 0) {
3323 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
3324 s_append_asprintf(errbuf, "Line %d: strange number of alpha electrons: %s", lineNumber, tokens[1]);
3329 } else if (strcmp(buf, "Number of beta electrons") == 0) {
3330 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
3331 s_append_asprintf(errbuf, "Line %d: strange number of beta electrons: %s", lineNumber, tokens[1]);
3336 if (bset->ne_alpha + bset->ne_beta != nelec) {
3337 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);
3341 } else if (strcmp(buf, "Number of basis functions") == 0) {
3342 if (tokens[1] == NULL || (nbasis = atoi(tokens[1])) <= 0) {
3343 s_append_asprintf(errbuf, "Line %d: strange number of basis functions: %s", lineNumber, tokens[1]);
3347 } else if (strcmp(buf, "Atomic numbers") == 0) {
3348 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
3349 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
3353 if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms, fp, &lineNumber) != 0) {
3354 s_append_asprintf(errbuf, "Line %d: cannot read atomic numbers", lineNumber);
3358 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
3359 ap->atomicNumber = iary[i];
3360 bset->nuccharges[i] = iary[i];
3361 ElementToString(ap->atomicNumber, ap->element);
3362 memmove(ap->aname, ap->element, 4);
3363 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
3368 } else if (strcmp(buf, "Nuclear charges") == 0) {
3369 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
3370 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
3374 if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms, fp, &lineNumber) != 0) {
3375 s_append_asprintf(errbuf, "Line %d: cannot read nuclear charges", lineNumber);
3379 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
3380 bset->nuccharges[i] = dary[i];
3384 } else if (strcmp(buf, "Current cartesian coordinates") == 0) {
3385 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * 3) {
3386 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of cartesian coordinates: %s", lineNumber, tokens[2]);
3390 if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms * 3, fp, &lineNumber) != 0) {
3391 s_append_asprintf(errbuf, "Line %d: cannot read cartesian coordinates", lineNumber);
3395 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
3396 ap->r.x = dary[i * 3] * kBohr2Angstrom;
3397 ap->r.y = dary[i * 3 + 1] * kBohr2Angstrom;
3398 ap->r.z = dary[i * 3 + 2] * kBohr2Angstrom;
3402 } else if (strcmp(buf, "MxBond") == 0) {
3403 if (tokens[1] == NULL || (mxbond = atoi(tokens[1])) <= 0) {
3404 s_append_asprintf(errbuf, "Line %d: strange number of bonds per atom: %s", lineNumber, tokens[1]);
3408 } else if (strcmp(buf, "IBond") == 0) {
3410 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * mxbond) {
3411 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of bonds: %s", lineNumber, tokens[2]);
3415 if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms * mxbond, fp, &lineNumber) != 0) {
3416 s_append_asprintf(errbuf, "Line %d: cannot read bond information", lineNumber);
3420 bonds = (Int *)malloc(sizeof(Int) * (mxbond * 2 + 1));
3421 for (i = 0; i < natoms; i++) {
3422 for (j = k = 0; j < mxbond; j++) {
3423 n = iary[i * mxbond + j] - 1;
3425 /* Connect atom i and atom n */
3431 bonds[k] = kInvalidIndex;
3432 MoleculeAddBonds(mp, k / 2, bonds, NULL, 1);
3438 } else if (strcmp(buf, "Shell types") == 0) {
3439 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0) {
3440 s_append_asprintf(errbuf, "Line %d: wrong number of shell types: %s", lineNumber, tokens[2]);
3444 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
3445 s_append_asprintf(errbuf, "Line %d: cannot read shell types", lineNumber);
3449 /* Allocate ShellInfo table and store shell type information */
3450 AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), nary - 1, NULL);
3451 for (i = n = 0, sp = bset->shells; i < nary; i++, sp++) {
3453 case 0: sp->sym = kGTOType_S; sp->ncomp = 1; break;
3454 case 1: sp->sym = kGTOType_P; sp->ncomp = 3; break;
3455 case -1: sp->sym = kGTOType_SP; sp->ncomp = 4; break;
3456 case 2: sp->sym = kGTOType_D; sp->ncomp = 6; break;
3457 case -2: sp->sym = kGTOType_D5; sp->ncomp = 5; break;
3458 case 3: sp->sym = kGTOType_F; sp->ncomp = 10; break;
3459 case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break;
3460 case 4: sp->sym = kGTOType_G; sp->ncomp = 15; break;
3461 case -4: sp->sym = kGTOType_G9; sp->ncomp = 9; break;
3463 s_append_asprintf(errbuf, "Line %d: unsupported shell type %d", lineNumber, iary[i]);
3470 bset->ncomps = ncomps = n;
3473 } else if (strcmp(buf, "Number of primitives per shell") == 0) {
3474 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
3475 s_append_asprintf(errbuf, "Line %d: wrong size of the primitive table: %s", lineNumber, tokens[2]);
3479 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
3480 s_append_asprintf(errbuf, "Line %d: cannot read primitive table", lineNumber);
3484 for (i = n = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3485 sp->nprim = iary[i];
3492 } else if (strcmp(buf, "Shell to atom map") == 0) {
3493 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
3494 s_append_asprintf(errbuf, "Line %d: wrong size of the shell-to-atom map: %s", lineNumber, tokens[2]);
3498 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
3499 s_append_asprintf(errbuf, "Line %d: cannot read shell-to-atom table", lineNumber);
3503 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3504 sp->a_idx = iary[i] - 1;
3508 } else if (strcmp(buf, "Primitive exponents") == 0) {
3509 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != nprims) {
3510 s_append_asprintf(errbuf, "Line %d: wrong number of primitive exponents: %s", lineNumber, tokens[2]);
3514 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3515 s_append_asprintf(errbuf, "Line %d: cannot read primitive exponents", lineNumber);
3519 /* Allocate PrimInfo table */
3520 AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), nprims - 1, NULL);
3521 for (i = 0, pp = bset->priminfos; i < nprims; i++, pp++) {
3526 } else if (strcmp(buf, "Contraction coefficients") == 0) {
3527 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
3528 s_append_asprintf(errbuf, "Line %d: wrong number of contraction coefficients: %s", lineNumber, tokens[2]);
3532 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3533 s_append_asprintf(errbuf, "Line %d: cannot read contraction coefficients", lineNumber);
3537 for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
3542 } else if (strcmp(buf, "P(S=P) Contraction coefficients") == 0) {
3543 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
3544 s_append_asprintf(errbuf, "Line %d: wrong number of P(S=P) contraction coefficients: %s", lineNumber, tokens[2]);
3548 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3549 s_append_asprintf(errbuf, "Line %d: cannot read P(S=P) contraction coefficients", lineNumber);
3553 for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
3558 } else if (strcmp(buf, "Alpha Orbital Energies") == 0) {
3559 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
3560 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha orbitals: %s", lineNumber, tokens[2]);
3564 if (sReadNumberArray(&bset->moenergies, &bset->nmos, sizeof(Double), i, fp, &lineNumber) != 0) {
3565 s_append_asprintf(errbuf, "Line %d: cannot read alpha orbital energies", lineNumber);
3569 } else if (strcmp(buf, "Alpha MO coefficients") == 0) {
3570 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
3571 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha MO coefficients: %s", lineNumber, tokens[2]);
3575 if (sReadNumberArray(&bset->mo, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3576 s_append_asprintf(errbuf, "Line %d: cannot read MO coefficients", lineNumber);
3580 } else if (strcmp(buf, "Beta Orbital Energies") == 0) {
3581 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
3582 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta orbitals: %s", lineNumber, tokens[2]);
3586 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3587 s_append_asprintf(errbuf, "Line %d: cannot read beta orbital energies", lineNumber);
3591 bset->moenergies = (Double *)realloc(bset->moenergies, sizeof(Double) * 2 * ncomps);
3592 bset->nmos = ncomps * 2;
3593 bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);
3594 memmove(bset->moenergies + ncomps, dary, sizeof(Double) * ncomps);
3595 memset(bset->mo + ncomps * ncomps, 0, sizeof(Double) * ncomps * ncomps);
3598 } else if (strcmp(buf, "Beta MO coefficients") == 0) {
3599 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
3600 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta MO coefficients: %s", lineNumber, tokens[2]);
3604 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3605 s_append_asprintf(errbuf, "Line %d: cannot read alpha MO coefficients", lineNumber);
3609 bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps); /* Should be unnecessary, just in case */
3610 memmove(bset->mo + ncomps * ncomps, dary, sizeof(Double) * ncomps * ncomps);
3613 } else if (strcmp(buf, "Total SCF Density") == 0) {
3614 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * (ncomps + 1) / 2) {
3615 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of SCF densities: %s", lineNumber, tokens[2]);
3619 if (sReadNumberArray(&bset->scfdensities, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3620 s_append_asprintf(errbuf, "Line %d: cannot read SCF densities", lineNumber);
3626 if (mp->natoms == 0) {
3627 s_append_asprintf(errbuf, "Atom information is missing");
3631 if (bset->shells == NULL || bset->priminfos == NULL) {
3632 s_append_asprintf(errbuf, "Gaussian primitive information is missing");
3636 if (bset->mo == NULL) {
3637 s_append_asprintf(errbuf, "MO coefficients were not found");
3641 if (sSetupGaussianCoefficients(bset) != 0) {
3642 s_append_asprintf(errbuf, "Internal error during setup MO calculation");
3655 if (mp->bset != NULL) {
3656 BasisSetRelease(mp->bset);
3662 Panic("low memory while reading fchk file %s", fname);
3663 return -1; /* not reached */
3667 MoleculeLoadGamessDatFile(Molecule *mol, const char *fname, char **errbuf)
3672 int lineNumber, i, j, k, len, natoms = 0;
3679 Vector *vbuf = NULL;
3681 int optimizing = 0, status = 0;
3685 mol = MoleculeNew();
3687 if (mol->natoms == 0)
3690 fp = fopen(fname, "rb");
3692 s_append_asprintf(errbuf, "Cannot open file");
3696 /* ESP is cleared (not undoable!) */
3697 if (mol->elpots != NULL) {
3704 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3707 if (strncmp(buf, " $DATA", 6) == 0) {
3708 /* Initial geometry */
3710 vbuf = (Vector *)calloc(sizeof(Vector), mol->natoms);
3713 ReadLine(buf, sizeof buf, fp, &lineNumber); /* Title */
3714 ReadLine(buf, sizeof buf, fp, &lineNumber); /* Symmetry */
3715 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3716 if (strncmp(buf, " $END", 5) == 0)
3718 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3719 s_append_asprintf(errbuf, "Line %d: bad format in $DATA section", lineNumber);
3725 memset(&a, 0, sizeof(a));
3726 strncpy(a.aname, sval, 4);
3730 a.atomicNumber = (Int)dval[0];
3731 strncpy(a.element, ElementToString(a.atomicNumber, sval), 3);
3732 a.type = AtomTypeEncodeToUInt(a.element);
3733 a.weight = WeightForAtomicNumber(a.atomicNumber);
3734 MoleculeCreateAnAtom(mol, &a, mol->natoms);
3737 if (i >= mol->natoms) {
3738 s_append_asprintf(errbuf, "Line %d: too many atoms", lineNumber);
3742 if ((ap = ATOM_AT_INDEX(mol->atoms, i))->atomicNumber != dval[0]) {
3743 s_append_asprintf(errbuf, "Line %d: atomic number does not match", lineNumber);
3747 vbuf[i].x = dval[1];
3748 vbuf[i].y = dval[2];
3749 vbuf[i].z = dval[3];
3751 /* Skip until a blank line is found */
3752 /* 2013.6.11. Line including "PM3" is also recognized as the end of atom */
3753 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3754 for (j = 0; buf[j] == ' '; j++);
3755 if (buf[j] == '\n' || strncmp(buf + j, "PM3", 3) == 0)
3762 /* Set atom positions */
3764 if (natoms < mol->natoms) {
3765 s_append_asprintf(errbuf, "Line %d: too few atoms", lineNumber);
3769 ig = IntGroupNewWithPoints(0, natoms, -1);
3770 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, natoms, vbuf);
3771 IntGroupRelease(ig);
3774 vbuf = (Vector *)calloc(sizeof(Vector), natoms);
3775 nframes = MoleculeGetNumberOfFrames(mol);
3779 } else if (strstr(buf, "DATA FROM NSERCH") != NULL || (strstr(buf, "RESULTS FROM SUCCESSFUL") != NULL && (n1 = 1))) {
3780 /* Skip until the separator line is read (three or four lines) */
3784 s_append_asprintf(errbuf, "Line %d: the separator line at the top of the coordinates is not found: bad format?", lineNumber);
3788 ReadLine(buf, sizeof buf, fp, &lineNumber);
3789 } while (strstr(buf, "----------------------------") == NULL);
3790 for (i = 0; i < natoms; i++) {
3791 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3792 s_append_asprintf(errbuf, "Unexpected end of file in reading NSERCH data");
3796 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3797 s_append_asprintf(errbuf, "Line %d: bad format in NSERCH coordinate data", lineNumber);
3801 vbuf[i].x = dval[1];
3802 vbuf[i].y = dval[2];
3803 vbuf[i].z = dval[3];
3805 ig = IntGroupNewWithPoints(nframes, 1, -1);
3806 MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, natoms, vbuf, 0, NULL);
3807 IntGroupRelease(ig);
3810 optimizing = 1; /* Flag to skip reading the VEC group */
3814 } else if (strstr(buf, "E(UHF)") != NULL || (strstr(buf, "E(RHF)") != NULL && (n1 = 1)) || (strstr(buf, "E(ROHF)") != NULL && (n1 = 2))) {
3815 if (mol->bset == NULL) {
3816 i = MoleculeSetMOInfo(mol, n1, 0, 0);
3818 s_append_asprintf(errbuf, "Line %d: cannot allocate basis set internal buffer", lineNumber);
3823 } else if (strncmp(buf, " $VEC", 5) == 0) {
3825 /* Read the vec group */
3826 if (mol->bset == NULL || mol->bset->ncomps == 0)
3827 continue; /* Just ignore */
3829 continue; /* Ignore VEC group during optimization */
3830 coeffs = (Double *)calloc(sizeof(Double), mol->bset->ncomps);
3831 if (coeffs == NULL) {
3832 s_append_asprintf(errbuf, "Line %d: low memory during $VEC", lineNumber);
3837 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3839 if (strncmp(buf, " $END", 5) == 0)
3841 while ((j = 5 + (k % 5) * 15) <= len && buf[j] != 0 && buf[j] != '\n') {
3842 strncpy(sval, buf + j, 15);
3844 coeffs[k] = strtod(sval, NULL);
3849 if (k < mol->bset->ncomps)
3851 j = MoleculeSetMOCoefficients(mol, i + 1, -1000000, k, coeffs);
3853 s_append_asprintf(errbuf, "Line %d: cannot set coefficients for MO %d", lineNumber, i + 1);
3864 } else if ((strstr(buf, "ELECTRIC POTENTIAL") != NULL || strstr(buf, "ELECTROSTATIC POTENTIAL") != NULL) && strstr(buf, "ELPOTT") != NULL) {
3866 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3868 if (strstr(buf, "TOTAL NUMBER OF GRID POINTS") != NULL)
3870 if (sscanf(buf, "%d %lf %lf %lf %lf", &ival[0], &dval[0], &dval[1], &dval[2], &dval[3]) < 5)
3872 ep = AssignArray(&mol->elpots, &mol->nelpots, sizeof(Elpot), i, NULL);
3873 ep->pos.x = dval[0];
3874 ep->pos.y = dval[1];
3875 ep->pos.z = dval[2];
3880 goto redo; /* This section has no end line, so the last line should be processed again */
3881 else break; /* End of file encountered or interrupted */
3882 } /* TODO: read MOLPLT info if present */
3885 s_append_asprintf(errbuf, "User interrupt at line %d", lineNumber);
3891 if (mol->natoms > 0)
3892 retval = 0; /* Return the partially constructed molecule */
3893 if (newmol && mol->nbonds == 0) {
3896 MoleculeGuessBonds(mol, 0.0, &nbonds, &bonds);
3898 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
3906 MoleculeReadCoordinatesFromFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
3909 if (ftype == NULL || *ftype == 0) {
3911 cp = strrchr(fname, '.');
3915 cp = guessMoleculeType(fname);
3916 if (strcmp(cp, "???") != 0)
3920 if (strcasecmp(ftype, "pdb") == 0) {
3921 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3924 /* Try all formats once again */
3925 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3931 MoleculeReadCoordinatesFromPdbFile(Molecule *mp, const char *fname, char **errbuf)
3937 int i, j, new_unit, retval;
3945 fp = fopen(fname, "rb");
3947 s_append_asprintf(errbuf, "Cannot open file");
3950 /* flockfile(fp); */
3951 if (mp->natoms == 0)
3954 /* Allocate buffer for undo-capable modification */
3955 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
3956 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3957 /* Retain current position if the atom info is missing in the input file */
3963 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3964 if (strncmp(buf, "END", 3) == 0)
3966 if (strncmp(buf, "HETATM", 6) == 0 || strncmp(buf, "ATOM", 4) == 0) {
3968 Int serial, intCharge, resSeq;
3971 char segName[5], resName[4], atomName[5], resSeqStr[5], atomType[3], element[3], occStr[6];
3973 memset(&w, 0, sizeof(w));
3974 ReadFormat(buf, "x6 I5 x1 S4 x1 S3 x1 x1 S4 x1 x3 F8 F8 F8 S6 F6 x6 S4 S2 I2",
3975 &w.serial, w.atomName, w.resName, w.resSeqStr, &w.r.x, &w.r.y, &w.r.z,
3976 w.occStr, &w.temp, w.segName, w.element, &w.intCharge);
3977 if (w.atomName[0] == 0) {
3978 continue; /* Atom name is empty */
3980 /* A workaround for residue number >= 10000 (XPLOR style) */
3981 if (w.resSeqStr[0] >= 'A' && w.resSeqStr[0] <= 'Z') {
3982 w.resSeq = (w.resSeqStr[0] - 'A' + 10) * 1000 + atoi(w.resSeqStr + 1);
3984 w.resSeq = atoi(w.resSeqStr);
3986 if (w.element[0] == 0) {
3987 /* $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl */
3988 for (p = w.atomName; *p != 0; p++) {
3989 if (isalpha(*p) && *p != '_') {
3990 w.element[0] = toupper(*p);
3991 if (isalpha(p[1]) && p[1] != '_') {
3992 w.element[1] = toupper(p[1]);
4001 if (w.occStr[0] == 0)
4004 w.occ = atof(w.occStr);
4005 if (w.serial <= 0) {
4006 s_append_asprintf(errbuf, "line %d: non-positive atom number %d", lineNumber, w.serial);
4010 w.serial--; /* The internal atom number is 0-based */
4011 if (w.serial >= mp->natoms) {
4013 /* Create a new atom entry */
4014 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, w.serial, NULL);
4016 s_append_asprintf(errbuf, "line %d: the atom number %d does not exist in the structure file", lineNumber, w.serial+1);
4022 ap = ATOM_AT_INDEX(mp->atoms, w.serial);
4024 ap->occupancy = w.occ;
4025 ap->tempFactor = w.temp;
4026 if (w.segName[0] == 0)
4027 strncpy(w.segName, "MAIN", 4);
4028 strncpy(ap->segName, w.segName, 4);
4029 ap->resSeq = w.resSeq;
4030 strncpy(ap->resName, w.resName, 4);
4031 strncpy(ap->aname, w.atomName, 4);
4032 strncpy(ap->element, w.element, 2);
4034 ap->atomicNumber = ElementToInt(ap->element);
4035 ap->type = AtomTypeEncodeToUInt(ap->element);
4036 ap->weight = WeightForAtomicNumber(ap->atomicNumber);
4037 ap->intCharge = w.intCharge;
4038 if (ap->resSeq > 0) {
4039 if (ap->resSeq < mp->nresidues) {
4040 /* Update the resName according to residues[] */
4041 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
4043 /* Register the resName to residues[] */
4044 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, w.resName);
4048 strcpy(ap->resName, "XXX");
4049 if (mp->nresidues == 0)
4050 AssignArray(&mp->residues, &mp->nresidues, 4, 0, ap->resName);
4052 i = ElementToInt(ap->element);
4054 ap->weight = gElementParameters[i].weight;
4056 /* Not a new unit: only the atom position is updated */
4060 } else if (strncmp(buf, "CONECT", 6) == 0 && new_unit) {
4061 i = ReadFormat(buf, "x6 I5I5I5I5I5I5I5I5I5I5I5I5",
4062 ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
4063 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7,
4064 ibuf + 8, ibuf + 9, ibuf + 10, ibuf + 11);
4068 for (j = 0; j < i; j++) {
4069 if (ibuf[j] < 0 || ibuf[j] > mp->natoms) {
4070 s_append_asprintf(errbuf, "line %d: The CONECT record contains non-existent atom %d", lineNumber, ibuf[j]);
4073 } else if (ibuf[j] == 0)
4079 for (j = 1, bi = 0; j < i; j++) {
4080 if (ibuf[0] < ibuf[j]) {
4081 if (MoleculeLookupBond(mp, ibuf[0], ibuf[j]) >= 0) {
4082 s_append_asprintf(errbuf, "line %d: warning: duplicate bond %d-%d\n", lineNumber, ibuf[0], ibuf[j]);
4084 bbuf[bi * 2] = ibuf[0] - 1;
4085 bbuf[bi * 2 + 1] = ibuf[j] - 1;
4093 retval = MoleculeAddBonds(mp, bi, bbuf, NULL, 1);
4095 s_append_asprintf(errbuf, "line %d: bad bond specification", lineNumber);
4102 /* funlockfile(fp); */
4105 /* Renumber atoms if some atom number is unoccupied */
4106 int *old2new, oldidx, newidx;
4107 old2new = (int *)calloc(sizeof(int), mp->natoms);
4108 if (old2new == NULL) {
4109 s_append_asprintf(errbuf, "Out of memory");
4113 for (oldidx = newidx = 0; oldidx < mp->natoms; oldidx++) {
4114 ap = ATOM_AT_INDEX(mp->atoms, oldidx);
4115 if (ap->aname[0] != 0) {
4116 old2new[oldidx] = newidx;
4117 if (oldidx > newidx)
4118 memmove(ATOM_AT_INDEX(mp->atoms, newidx), ap, gSizeOfAtomRecord);
4122 mp->natoms = newidx;
4123 if (oldidx > newidx) {
4124 /* Renumber the connects and bonds */
4126 for (i = 0; i < mp->natoms; i++) {
4127 ap = ATOM_AT_INDEX(mp->atoms, i);
4128 cp = AtomConnectData(&ap->connect);
4129 for (j = 0; j < ap->connect.count; j++) {
4130 cp[j] = old2new[cp[j]];
4133 for (i = 0; i < mp->nbonds * 2; i++) {
4134 mp->bonds[i] = old2new[mp->bonds[i]];
4137 retval = MoleculeRebuildTablesFromConnects(mp);
4139 /* This error may not happen */
4140 s_append_asprintf(errbuf, "Cannot build angle/dihedral/improper tables");
4144 /* Undo action: delete all atoms */
4147 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
4148 act = MolActionNew(gMolActionUnmergeMolecule, ig);
4149 act->frame = mp->cframe;
4150 MolActionCallback_registerUndo(mp, act);
4151 MolActionRelease(act);
4152 IntGroupRelease(ig);
4155 /* Set the new atom positions */
4156 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
4157 MolActionCreateAndPerform(mp, gMolActionSetAtomPositions, ig, mp->natoms, vp);
4158 IntGroupRelease(ig);
4162 mp->nframes = -1; /* Should be recalculated later */
4164 return 1; /* No atoms */
4168 /* funlockfile(fp); */
4174 return 1; /* Maybe different format? */
4179 MoleculeReadCoordinatesFromDcdFile(Molecule *mp, const char *fname, char **errbuf)
4182 SFloat32 *xp, *yp, *zp;
4185 int n, errcount = 0;
4187 if (mp == NULL || mp->natoms == 0) {
4188 s_append_asprintf(errbuf, "Molecule is empty");
4191 n = DcdOpen(fname, &dcd);
4194 case -2: s_append_asprintf(errbuf, "Cannot open file"); break;
4195 case 1: s_append_asprintf(errbuf, "Premature EOF encountered"); break;
4196 case 2: s_append_asprintf(errbuf, "Bad block length of the first section"); break;
4197 case 3: s_append_asprintf(errbuf, "\"CORD\" signature is missing"); break;
4198 case 4: s_append_asprintf(errbuf, "Bad termination of the first section"); break;
4199 case 5: s_append_asprintf(errbuf, "The title section is not correct"); break;
4200 case 6: s_append_asprintf(errbuf, "The atom number section is not correct"); break;
4201 default: s_append_asprintf(errbuf, "Read error in dcd file"); break;
4205 if (dcd.natoms == 0) {
4206 s_append_asprintf(errbuf, "No atoms were found in the dcd file");
4208 } else if (dcd.nframes == 0) {
4209 s_append_asprintf(errbuf, "No frames were found in the dcd file");
4219 vp = (Vector *)calloc(sizeof(Vector), mp->natoms * dcd.nframes);
4221 cp = (Vector *)calloc(sizeof(Vector), dcd.nframes * 4);
4223 xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4224 yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4225 zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4226 ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mp), dcd.nframes, -1);
4227 if (vp == NULL || xp == NULL || yp == NULL || zp == NULL || ig == NULL) {
4228 s_append_asprintf(errbuf, "Cannot allocate memory");
4234 if (ig) IntGroupRelease(ig);
4237 for (n = 0; n < dcd.nframes; n++) {
4240 SFloat32 dcdcell[6];
4241 if (DcdReadFrame(&dcd, n, xp, yp, zp, dcdcell)) {
4242 s_append_asprintf(errbuf, "Read error in dcd file");
4245 for (i = 0, vpp = &vp[n * mp->natoms]; i < dcd.natoms && i < mp->natoms; i++, vpp++) {
4253 /* dcdcell = {a, gamma, b, beta, alpha, c} */
4254 /* angles are described either in cosines (Charmm and NAMD > 2.5) or degrees (NAMD 2.5) */
4255 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) {
4256 dcdcell[4] = cos(dcdcell[4] * kDeg2Rad); /* cos(alpha) */
4257 dcdcell[3] = cos(dcdcell[3] * kDeg2Rad); /* cos(beta) */
4258 dcdcell[1] = cos(dcdcell[1] * kDeg2Rad); /* cos(gamma) */
4260 /* a axis lies along the cartesian x axis */
4261 sing = sqrt(1 - dcdcell[1] * dcdcell[1]);
4262 vpp[0].x = dcdcell[0];
4265 vpp[1].x = dcdcell[2] * dcdcell[1];
4266 vpp[1].y = dcdcell[2] * sing;
4268 vpp[2].x = dcdcell[5] * dcdcell[3];
4269 vpp[2].y = dcdcell[5] * (dcdcell[4] - dcdcell[3] * dcdcell[1]) / sing;
4270 vpp[2].z = sqrt(dcdcell[5] * dcdcell[5] - vpp[2].x * vpp[2].x - vpp[2].y * vpp[2].y);
4271 vpp[3].x = vpp[3].y = vpp[3].z = 0.0;
4272 if (mp->cell == NULL) {
4273 /* Create periodicity if not present */
4274 MolActionCreateAndPerform(mp, gMolActionSetBox, &vpp[0], &vpp[1], &vpp[2], &vpp[3], 7, 0);
4278 if (MolActionCreateAndPerform(mp, gMolActionInsertFrames, ig, mp->natoms * dcd.nframes, vp, (cp == NULL ? 0 : dcd.nframes * 4), cp) != 0)
4279 s_append_asprintf(errbuf, "Cannot insert frames");
4280 mp->startStep = dcd.nstart;
4281 mp->stepsPerFrame = dcd.ninterval;
4282 mp->psPerStep = dcd.delta;
4291 IntGroupRelease(ig);
4298 MoleculeReadExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
4309 fp = fopen(fname, "rb");
4311 s_append_asprintf(errbuf, "Cannot open file");
4317 flags[0] = flags[1] = flags[2] = 0;
4318 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
4319 if (strncmp(buf, "Bounding box:", 13) == 0) {
4320 for (i = 0; i < 3; i++) {
4321 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
4322 s_append_asprintf(errbuf, "line %d: missing %d component of the bounding box", lineNumber, i + 1);
4326 n = sscanf(buf, "%lf %lf %lf %d", &d[0], &d[1], &d[2], &flag);
4328 vv.x = vv.y = vv.z = 0.0;
4330 case 0: vv.x = d[0]; break;
4331 case 1: vv.y = d[0]; break;
4332 case 2: vv.z = d[0]; break;
4334 if (n == 1 || (n == 2 && d[1] != 0.0))
4341 flags[i] = (flag != 0);
4343 flags[i] = (VecLength2(vv) != 0);
4347 if (mp->cell != NULL)
4348 vv = mp->cell->origin;
4350 vv.x = vv.y = vv.z = 0.0;
4351 MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
4352 } else if (strncmp(buf, "Bounding box origin:", 20) == 0) {
4353 if (mp->cell != NULL) {
4354 v[0] = mp->cell->axes[0];
4355 v[1] = mp->cell->axes[1];
4356 v[2] = mp->cell->axes[2];
4357 memmove(flags, mp->cell->flags, 3);
4359 v[0].x = 1.0; v[0].y = v[0].z = 0.0;
4360 v[1].y = 1.0; v[1].x = v[1].z = 0.0;
4361 v[2].z = 1.0; v[2].x = v[2].y = 0.0;
4362 flags[0] = flags[1] = flags[2] = 1.0;
4364 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0 || (n = sscanf(buf, "%lf %lf %lf", &d[0], &d[1], &d[2]) < 3)) {
4365 s_append_asprintf(errbuf, "line %d: wrong format for the bounding box origin", lineNumber);
4372 MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
4384 MoleculeWriteToFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
4388 if (ftype == NULL || *ftype == 0) {
4390 cp = strrchr(fname, '.');
4394 cp = guessMoleculeType(fname);
4395 if (strcmp(cp, "???") != 0)
4399 if (strcasecmp(ftype, "psf") == 0) {
4400 retval = MoleculeWriteToPsfFile(mp, fname, errbuf);
4401 } else if (strcasecmp(ftype, "pdb") == 0) {
4402 retval = MoleculeWriteToPdbFile(mp, fname, errbuf);
4403 } else if (strcasecmp(ftype, "tep") == 0) {
4404 retval = MoleculeWriteToTepFile(mp, fname, errbuf);
4406 s_append_asprintf(errbuf, "The file format should be specified");
4410 MoleculeSetPath(mp, fname);
4415 MoleculeWriteToMbsfFile(Molecule *mp, const char *fname, char **errbuf)
4418 Int i, j, k, n1, n2, n3, n_aniso, nframes, nanchors, n_uff;
4424 fp = fopen(fname, "wb");
4426 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4431 nframes = MoleculeFlushFrames(mp);
4433 fprintf(fp, "!:atoms\n");
4434 fprintf(fp, "! idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge\n");
4435 n1 = n2 = n3 = n_aniso = nanchors = n_uff = 0;
4436 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4437 strncpy(bufs[0], ap->segName, 4);
4439 strncpy(bufs[1], ap->resName, 4);
4441 strncpy(bufs[2], ap->aname, 4);
4443 AtomTypeDecodeToString(ap->type, bufs[3]);
4445 strncpy(bufs[4], ap->element, 4);
4447 for (j = 0; j < 5; j++) {
4448 if (bufs[j][0] == 0) {
4452 for (k = 0; k < 6; k++) {
4453 if (bufs[j][k] == 0)
4455 if (bufs[j][k] > 0 && bufs[j][k] < ' ')
4459 if (SYMOP_ALIVE(ap->symop))
4461 if (ap->fix_force != 0)
4463 if (ap->mm_exclude || ap->periodic_exclude)
4465 if (ap->aniso != NULL)
4467 if (ap->anchor != NULL)
4469 if (ap->uff_type[0] != 0)
4471 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);
4476 fprintf(fp, "!:uff_type\n");
4477 fprintf(fp, "! idx uff_type\n");
4478 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4479 fprintf(fp, "%d %.5s\n", i, ap->uff_type);
4485 fprintf(fp, "!:atoms_symop\n");
4486 fprintf(fp, "! idx symop symbase\n");
4487 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4489 n = ap->symop.sym * 1000000 + ap->symop.dx * 10000 + ap->symop.dy * 100 + ap->symop.dz;
4490 fprintf(fp, "%d %d %d\n", i, n, ap->symbase);
4496 fprintf(fp, "!:atoms_fix\n");
4497 fprintf(fp, "! idx fix_force fix_pos\n");
4498 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4499 fprintf(fp, "%d %f %f %f %f\n", i, ap->fix_force, ap->fix_pos.x, ap->fix_pos.y, ap->fix_pos.z);
4505 fprintf(fp, "!:mm_exclude\n");
4506 fprintf(fp, "! idx mm_exclude periodic_exclude\n");
4507 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4508 fprintf(fp, "%d %d %d\n", i, ap->mm_exclude, ap->periodic_exclude);
4514 fprintf(fp, "!:pi_anchor\n");
4515 fprintf(fp, "! idx count; n1 weight1; n2 weight2; ...; nN weightN\n");
4516 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4518 if (ap->anchor == NULL)
4520 k = ap->anchor->connect.count;
4521 ip = AtomConnectData(&ap->anchor->connect);
4522 fprintf(fp, "%d %d\n", i, k);
4523 for (j = 0; j < k; j++) {
4524 fprintf(fp, "%d %f\n", ip[j], ap->anchor->coeffs[j]);
4535 for (i = 0; (i == n2 || i < n1); i++) {
4536 fprintf(fp, "!:positions ; frame %d\n", i);
4537 fprintf(fp, "! idx x y z [sx sy sz]\n");
4538 for (j = 0, ap = mp->atoms; j < mp->natoms; j++, ap = ATOM_NEXT(ap)) {
4541 if (i != n2 && i < ap->nframes)
4542 vp = ap->frames + i;
4545 if (ap->sigma.x != 0.0 || ap->sigma.y != 0.0 || ap->sigma.z != 0.0)
4548 fprintf(fp, "%d %.8f %.8f %.8f", j, vp->x, vp->y, vp->z);
4550 fprintf(fp, " %.8f %.8f %.8f", ap->sigma.x, ap->sigma.y, ap->sigma.z);
4557 if (mp->nbonds > 0) {
4558 fprintf(fp, "!:bonds\n");
4559 fprintf(fp, "! from1 to1 from2 to2 from3 to3 from4 to4\n");
4560 for (i = 0; i < mp->nbonds; i++) {
4561 fprintf(fp, "%d %d%c", mp->bonds[i * 2], mp->bonds[i * 2 + 1], (i % 4 == 3 || i == mp->nbonds - 1 ? '\n' : ' '));
4566 if (mp->nbondOrders > 0) {
4567 fprintf(fp, "!:bond_orders\n");
4568 fprintf(fp, "! order1 order2 order3 order4\n");
4569 for (i = 0; i < mp->nbondOrders; i++) {
4570 fprintf(fp, "%.6f%c", mp->bondOrders[i], (i % 4 == 3 || i == mp->nbondOrders - 1 ? '\n' : ' '));
4575 if (mp->nangles > 0) {
4576 fprintf(fp, "!:angles\n");
4577 fprintf(fp, "! a1 b1 c1 a2 b2 c2 a3 b3 c3\n");
4578 for (i = 0; i < mp->nangles; i++) {
4579 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' : ' '));
4584 if (mp->ndihedrals > 0) {
4585 fprintf(fp, "!:dihedrals\n");
4586 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
4587 for (i = 0; i < mp->ndihedrals; i++) {
4588 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' : ' '));
4593 if (mp->nimpropers > 0) {
4594 fprintf(fp, "!:impropers\n");
4595 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
4596 for (i = 0; i < mp->nimpropers; i++) {
4597 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' : ' '));
4602 if (mp->cell != NULL) {
4603 fprintf(fp, "!:xtalcell\n");
4604 fprintf(fp, "! a b c alpha beta gamma\n");
4605 fprintf(fp, "! This information is redundant and overridden by the following periodic_box info\n");
4606 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]);
4609 fprintf(fp, "!:periodic_box\n");
4610 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");
4611 for (i = 0; i < 3; i++)
4612 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->axes[i].x, mp->cell->axes[i].y, mp->cell->axes[i].z);
4613 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->origin.x, mp->cell->origin.y, mp->cell->origin.z);
4614 fprintf(fp, "%d %d %d%s\n", mp->cell->flags[0], mp->cell->flags[1], mp->cell->flags[2], (mp->cell->has_sigma ? " 1" : ""));
4615 if (mp->cell->has_sigma) {
4616 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]);
4621 if (mp->nframe_cells > 0) {
4622 fprintf(fp, "!:frame_periodic_boxes\n");
4623 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz\n");
4624 for (i = 0; i < mp->nframe_cells * 4; i++) {
4625 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->frame_cells[i].x, mp->frame_cells[i].y, mp->frame_cells[i].z);
4630 if (mp->nsyms > 0) {
4631 fprintf(fp, "!:symmetry_operations\n");
4632 fprintf(fp, "! a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3\n");
4633 for (i = 0; i < mp->nsyms; i++) {
4634 Transform *tp = mp->syms + i;
4635 const unsigned char s_index_order[12] = {0, 3, 6, 1, 4, 7, 2, 5, 8, 9, 10, 11};
4636 for (j = 0; j < 12; j++)
4637 fprintf(fp, "%11.6f%c", (*tp)[s_index_order[j]], (j % 3 == 2 ? '\n' : ' '));
4643 fprintf(fp, "!:anisotropic_thermal_parameters\n");
4644 fprintf(fp, "! b11 b22 b33 b12 b13 b23 [sigma; sb11 sb22 sb33 sb12 sb13 sb23]\n");
4645 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4646 if (ap->aniso != NULL) {
4647 Double *bp = ap->aniso->bij;
4648 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" : ""));
4649 if (ap->aniso->has_bsig) {
4650 bp = ap->aniso->bsig;
4651 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]);
4654 fprintf(fp, "0 0 0 0 0 0\n");
4660 if (mp->arena != NULL) {
4661 MDArena *arena = mp->arena;
4662 fprintf(fp, "!:md_parameters\n");
4663 fprintf(fp, "log_file %s\n", arena->log_result_name);
4664 fprintf(fp, "coord_file %s\n", arena->coord_result_name);
4665 fprintf(fp, "vel_file %s\n", arena->vel_result_name);
4666 fprintf(fp, "force_file %s\n", arena->force_result_name);
4667 fprintf(fp, "debug_file %s\n", arena->debug_result_name);
4668 fprintf(fp, "debug_output_level %d\n", arena->debug_output_level);
4669 fprintf(fp, "step %d\n", arena->step);
4670 fprintf(fp, "coord_output_freq %d\n", arena->coord_output_freq);
4671 fprintf(fp, "energy_output_freq %d\n", arena->energy_output_freq);
4672 fprintf(fp, "coord_frame %d\n", arena->coord_result_frame);
4673 fprintf(fp, "timestep %g\n", arena->timestep);
4674 fprintf(fp, "cutoff %g\n", arena->cutoff);
4675 fprintf(fp, "electro_cutoff %g\n", arena->electro_cutoff);
4676 fprintf(fp, "pairlist_distance %g\n", arena->pairlist_distance);
4677 fprintf(fp, "switch_distance %g\n", arena->switch_distance);
4678 fprintf(fp, "temperature %g\n", arena->temperature);
4679 fprintf(fp, "andersen_freq %d\n", arena->andersen_thermo_freq);
4680 fprintf(fp, "andersen_coupling %g\n", arena->andersen_thermo_coupling);
4681 fprintf(fp, "random_seed %d\n", arena->random_seed);
4682 fprintf(fp, "dielectric %g\n", arena->dielectric);
4683 fprintf(fp, "gradient_convergence %g\n", arena->gradient_convergence);
4684 fprintf(fp, "coordinate_convergence %g\n", arena->coordinate_convergence);
4685 fprintf(fp, "use_xplor_shift %d\n", arena->use_xplor_shift);
4686 fprintf(fp, "scale14_vdw %g\n", arena->scale14_vdw);
4687 fprintf(fp, "scale14_elect %g\n", arena->scale14_elect);
4688 fprintf(fp, "relocate_center %d\n", arena->relocate_center);
4689 fprintf(fp, "surface_probe_radius %g\n", arena->probe_radius);
4690 fprintf(fp, "surface_tension %g\n", arena->surface_tension);
4691 fprintf(fp, "surface_potential_freq %d\n", arena->surface_potential_freq);
4692 fprintf(fp, "use_graphite %d\n", arena->use_graphite);
4693 fprintf(fp, "alchemical_lambda %g\n", arena->alchem_lambda);
4694 fprintf(fp, "alchemical_delta_lambda %g\n", arena->alchem_dlambda);
4695 if (arena->nalchem_flags > 0) {
4696 fprintf(fp, "alchem_flags %d", arena->nalchem_flags);
4697 for (i = 0; i < arena->nalchem_flags; i++) {
4700 else if (i % 10 == 0)
4702 fputc('0' + arena->alchem_flags[i], fp);
4706 if (arena->pressure != NULL) {
4708 fprintf(fp, "pressure_freq %d\n", arena->pressure->freq);
4709 fprintf(fp, "pressure_coupling %g\n", arena->pressure->coupling);
4710 dp = arena->pressure->apply;
4711 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]);
4712 dp = arena->pressure->cell_flexibility;
4713 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]);
4714 fprintf(fp, "pressure_fluctuate_cell_origin %g\n", arena->pressure->fluctuate_cell_origin);
4715 fprintf(fp, "pressure_fluctuate_cell_orientation %g\n", arena->pressure->fluctuate_cell_orientation);
4719 if (mp->par != NULL) {
4720 Parameter *par = mp->par;
4721 fprintf(fp, "!:parameters\n");
4722 ParameterAppendToFile(par, fp);
4726 fprintf(fp, "!:velocity\n");
4727 fprintf(fp, "! idx vx vy vz\n");
4728 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4729 fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->v.x, ap->v.y, ap->v.z);
4733 fprintf(fp, "!:force\n");
4734 fprintf(fp, "! idx fx fy fz\n");
4735 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4736 fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->f.x, ap->f.y, ap->f.z);
4741 if (mp->mview != NULL) {
4743 if (mp->mview->track != NULL) {
4744 fprintf(fp, "!:trackball\n");
4745 fprintf(fp, "! scale; trx try trz; theta_deg x y z\n");
4746 f[0] = TrackballGetScale(mp->mview->track);
4747 fprintf(fp, "%f\n", f[0]);
4748 TrackballGetTranslate(mp->mview->track, f);
4749 fprintf(fp, "%f %f %f\n", f[0], f[1], f[2]);
4750 TrackballGetRotate(mp->mview->track, f);
4751 fprintf(fp, "%f %f %f %f\n", f[0], f[1], f[2], f[3]);
4754 fprintf(fp, "!:view\n");
4755 fprintf(fp, "show_unit_cell %d\n", mp->mview->showUnitCell);
4756 fprintf(fp, "show_periodic_box %d\n", mp->mview->showPeriodicBox);
4757 fprintf(fp, "show_expanded_atoms %d\n", mp->mview->showExpandedAtoms);
4758 fprintf(fp, "show_ellipsoids %d\n", mp->mview->showEllipsoids);
4759 fprintf(fp, "show_hydrogens %d\n", mp->mview->showHydrogens);
4760 fprintf(fp, "show_dummy_atoms %d\n", mp->mview->showDummyAtoms);
4761 fprintf(fp, "show_rotation_center %d\n", mp->mview->showRotationCenter);
4762 fprintf(fp, "show_graphite_flag %d\n", mp->mview->showGraphiteFlag);
4763 fprintf(fp, "show_graphite %d\n", mp->mview->showGraphite);
4764 fprintf(fp, "show_periodic_image_flag %d\n", mp->mview->showPeriodicImageFlag);
4765 fprintf(fp, "show_periodic_image %d %d %d %d %d %d\n",
4766 mp->mview->showPeriodicImage[0], mp->mview->showPeriodicImage[1],
4767 mp->mview->showPeriodicImage[2], mp->mview->showPeriodicImage[3],
4768 mp->mview->showPeriodicImage[4], mp->mview->showPeriodicImage[5]);
4769 if (mp->mview->atomRadius != 0.2)
4770 fprintf(fp, "atom_radius %f\n", mp->mview->atomRadius);
4771 if (mp->mview->bondRadius != 0.1)
4772 fprintf(fp, "bond_radius %f\n", mp->mview->bondRadius);
4773 if (mp->mview->atomResolution != 12)
4774 fprintf(fp, "atom_resolution %d\n", mp->mview->atomResolution);
4775 if (mp->mview->bondResolution != 8)
4776 fprintf(fp, "bond_resolution %d\n", mp->mview->bondResolution);
4780 if (mp->nmolprops > 0) {
4782 for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
4783 /* Encode the property name if necessary */
4786 for (p = prp->propname; *p != 0 && n1 < 900; p++) {
4787 if (*p > ' ' && *p != '%' && *p < 0x7f) {
4791 sprintf(enc + n1, "%%%02x", *p);
4798 enc[n2] = 0; /* Truncate after last ASCII character */
4802 sprintf(enc, "prop_%d", i + 1);
4805 fprintf(fp, "!:property ; %s\n", enc);
4806 for (j = 0; j < nframes; j++) {
4807 fprintf(fp, "%.18g\n", prp->propvals[j]);
4813 if (mp->bset != NULL) {
4814 /* Gaussian primitive info */
4817 fprintf(fp, "!:gaussian_primitives\n");
4818 fprintf(fp, "! sym nprims a_idx [add_exp]; A C Csp\n");
4819 for (i = 0, sp = mp->bset->shells; i < mp->bset->nshells; i++, sp++) {
4821 case kGTOType_S: p = "S"; break;
4822 case kGTOType_P: p = "P"; break;
4823 case kGTOType_SP: p = "SP"; break;
4824 case kGTOType_D: p = "D"; break;
4825 case kGTOType_D5: p = "D5"; break;
4826 case kGTOType_F: p = "F"; break;
4827 case kGTOType_F7: p = "F7"; break;
4828 case kGTOType_G: p = "G"; break;
4829 case kGTOType_G9: p = "G9"; break;
4830 default: snprintf(bufs[0], 8, "X%d", sp->sym); p = bufs[0]; break;
4832 if (sp->add_exp == 0)
4833 fprintf(fp, "%s %d %d\n", p, sp->nprim, sp->a_idx);
4835 fprintf(fp, "%s %d %d %d\n", p, sp->nprim, sp->a_idx, sp->add_exp);
4836 pp = mp->bset->priminfos + sp->p_idx;
4837 for (j = 0; j < sp->nprim; j++, pp++) {
4838 fprintf(fp, "%.18g %.18g %.18g\n", pp->A, pp->C, pp->Csp);
4844 fprintf(fp, "!:mo_info\n");
4845 fprintf(fp, "! uhf|rhf|rohf ne_alpha ne_beta\n");
4846 switch (mp->bset->rflag) {
4847 case 0: p = "UHF"; break;
4848 case 1: p = "RHF"; break;
4849 case 2: p = "ROHF"; break;
4850 default: p = "(unknown)"; break;
4852 fprintf(fp, "%s %d %d\n", p, mp->bset->ne_alpha, mp->bset->ne_beta);
4855 /* MO coefficients */
4856 fprintf(fp, "!:mo_coefficients\n");
4857 for (i = 0; i < mp->bset->nmos; i++) {
4858 fprintf(fp, "MO %d %.18g\n", i + 1, mp->bset->moenergies[i]);
4859 for (j = 0; j < mp->bset->ncomps; j++) {
4860 fprintf(fp, "%.18g%c", mp->bset->mo[i * mp->bset->ncomps + j], (j % 6 == 5 || j == mp->bset->ncomps - 1 ? '\n' : ' '));
4866 if (mp->mview != NULL && mp->mview->ngraphics > 0) {
4867 MainViewGraphic *gp;
4868 fprintf(fp, "!:graphics\n");
4869 for (i = 0; i < mp->mview->ngraphics; i++) {
4870 gp = mp->mview->graphics + i;
4872 case kMainViewGraphicLine: fprintf(fp, "line\n"); break;
4873 case kMainViewGraphicPoly: fprintf(fp, "poly\n"); break;
4874 case kMainViewGraphicCylinder: fprintf(fp, "cylinder\n"); break;
4875 case kMainViewGraphicCone: fprintf(fp, "cone\n"); break;
4876 case kMainViewGraphicEllipsoid: fprintf(fp, "ellipsoid\n"); break;
4877 default: fprintf(fp, "unknown\n"); break;
4879 fprintf(fp, "%d %d\n", gp->closed, gp->visible);
4880 fprintf(fp, "%.4f %.4f %.4f %.4f\n", gp->rgba[0], gp->rgba[1], gp->rgba[2], gp->rgba[3]);
4881 fprintf(fp, "%d\n", gp->npoints);
4882 for (j = 0; j < gp->npoints; j++)
4883 fprintf(fp, "%.6f %.6f %.6f\n", gp->points[j * 3], gp->points[j * 3 + 1], gp->points[j * 3 + 2]);
4884 fprintf(fp, "%d\n", gp->nnormals);
4885 for (j = 0; j < gp->nnormals; j++)
4886 fprintf(fp, "%.6f %.6f %.6f\n", gp->normals[j * 3], gp->normals[j * 3 + 1], gp->normals[j * 3 + 2]);
4891 /* Plug-in in the Ruby world */
4894 /* Save NBO information */
4895 if (MolActionCreateAndPerform(mp, SCRIPT_ACTION(";s"),
4896 "proc { nbo2msbfstring rescue \"Plug-in error: #{$!.to_s}\" }", &outMessage) == 0) {
4897 if (strncmp(outMessage, "Plug-in error", 13) == 0) {
4898 s_append_asprintf(errbuf, "%s", outMessage);
4900 fputs(outMessage, fp);
4904 if (MolActionCreateAndPerform(mp, SCRIPT_ACTION(";s"),
4905 "proc { savembsf_plugin rescue \"Plug-in error: #{$!.to_s}\" }", &outMessage) == 0) {
4906 if (outMessage[0] != 0) {
4907 if (strncmp(outMessage, "Plug-in error", 13) == 0) {
4908 s_append_asprintf(errbuf, "%s", outMessage);
4910 fprintf(fp, "%s\n", outMessage);
4922 MoleculeWriteToPsfFile(Molecule *mp, const char *fname, char **errbuf)
4928 fp = fopen(fname, "wb");
4930 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4933 fprintf(fp, "PSF\n\n");
4934 fprintf(fp, " 1 !NTITLE\n");
4935 fprintf(fp, " REMARKS FILENAME=\n");
4939 fprintf(fp, "%8d !NATOM\n", mp->natoms);
4940 for (i = 0; i < mp->natoms; i++) {
4942 ap = ATOM_AT_INDEX(mp->atoms, i);
4943 fprintf(fp, "%8d ", i + 1);
4944 if (ap->resSeq >= 10000) {
4945 fmt = "%-3.3s %-5d ";
4947 fmt = "%-4.4s %-4d ";
4949 fprintf(fp, fmt, ap->segName, ap->resSeq);
4950 fprintf(fp, "%-3.3s %-4.4s %-4.4s %12.6f %8.4f 0\n",
4951 ap->resName, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->charge, ap->weight);
4956 fprintf(fp, "%8d !NBOND: bonds\n", mp->nbonds);
4957 for (i = 0; i < mp->nbonds * 2; i++) {
4958 fprintf(fp, "%8d", mp->bonds[i] + 1);
4967 fprintf(fp, "%8d !NTHETA: angles\n", mp->nangles);
4968 for (i = 0; i < mp->nangles * 3; i++) {
4969 fprintf(fp, "%8d", mp->angles[i] + 1);
4978 fprintf(fp, "%8d !NPHI: dihedrals\n", mp->ndihedrals);
4979 for (i = 0; i < mp->ndihedrals * 4; i++) {
4980 fprintf(fp, "%8d", mp->dihedrals[i] + 1);
4989 fprintf(fp, "%8d !NIMPHI: impropers\n", mp->nimpropers);
4990 for (i = 0; i < mp->nimpropers * 4; i++) {
4991 fprintf(fp, "%8d", mp->impropers[i] + 1);
4999 fprintf(fp, "%8d !NDON: donors\n\n", 0);
5000 fprintf(fp, "%8d !NACC: acceptors\n\n", 0);
5001 fprintf(fp, "%8d !NNB: non-bonding exclusions\n\n", 0);
5002 for (i = 0; i < mp->natoms; i++) {
5003 fprintf(fp, "%8d", 0);
5010 fprintf(fp, "%8d !NGRP: groups\n", 1);
5011 fprintf(fp, " 0 0 0\n");
5015 if (i > 5 && strcmp(fname + i - 5, ".psfx") == 0) {
5016 /* Extended psf (with coordinates and other info) */
5017 fprintf(fp, "%8d !COORD: coordinates\n", mp->natoms);
5018 for (i = 0; i < mp->natoms; i++) {
5020 ap = ATOM_AT_INDEX(mp->atoms, i);
5022 fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->aname);
5032 MoleculeWriteToPdbFile(Molecule *mp, const char *fname, char **errbuf)
5038 fp = fopen(fname, "wb");
5040 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
5043 for (i = 0; i < mp->natoms; i++) {
5045 ap = ATOM_AT_INDEX(mp->atoms, i);
5046 if (ap->resSeq >= 10000) {
5047 snprintf(buf, sizeof buf, "%c%03d", 'A' + (ap->resSeq - 10000) / 1000, ap->resSeq % 1000);
5049 snprintf(buf, sizeof buf, "%4d", ap->resSeq);
5051 fprintf(fp, "ATOM %5d %-4.4s%1.1s%-3.3s %1.1s%4.4s%1.1s "
5052 "%8.3f%8.3f%8.3f %5.2f %5.2f "
5053 "%-4.4s%-2.2s%-2d\n",
5054 i + 1, ap->aname, " ", ap->resName, " ", buf, " ",
5055 ap->r.x, ap->r.y, ap->r.z, ap->occupancy, ap->tempFactor,
5056 ap->segName, ap->element, ap->intCharge);
5058 for (i = 0; i < mp->natoms; i++) {
5060 ap = ATOM_AT_INDEX(mp->atoms, i);
5061 cp = AtomConnectData(&ap->connect);
5062 for (j = 0; j < ap->connect.count; j++) {
5066 fprintf(fp, "CONECT%5d", i + 1);
5068 fprintf(fp, "%5d", cp[j] + 1);
5073 fprintf(fp, "END\n");
5079 MoleculeWriteToDcdFile(Molecule *mp, const char *fname, char **errbuf)
5082 SFloat32 *xp, *yp, *zp;
5085 if (mp == NULL || mp->natoms == 0) {
5086 s_append_asprintf(errbuf, "Molecule is empty");
5089 memset(&dcd, 0, sizeof(dcd));
5090 dcd.natoms = mp->natoms;
5091 dcd.nframes = MoleculeGetNumberOfFrames(mp);
5092 if (dcd.nframes == 0) {
5093 s_append_asprintf(errbuf, "no frame is present");
5096 dcd.nstart = mp->startStep;
5097 dcd.ninterval = mp->stepsPerFrame;
5098 if (dcd.ninterval == 0)
5100 dcd.nend = dcd.nstart + (dcd.nframes - 1) * dcd.ninterval;
5101 if (mp->cell != NULL)
5103 dcd.delta = mp->psPerStep;
5104 if (dcd.delta == 0.0)
5107 n = DcdCreate(fname, &dcd);
5110 s_append_asprintf(errbuf, "Cannot create dcd file");
5112 s_append_asprintf(errbuf, "Cannot write dcd header");
5117 xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
5118 yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
5119 zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
5120 if (xp == NULL || yp == NULL || zp == NULL) {
5121 s_append_asprintf(errbuf, "Cannot allocate memory");
5128 for (n = 0; n < dcd.nframes; n++) {
5131 for (i = 0, ap = mp->atoms; i < dcd.natoms && mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5133 if (ap->frames == NULL || n >= ap->nframes)
5141 if (i < dcd.natoms) {
5142 size_t sz = (dcd.natoms - i) * sizeof(SFloat32);
5143 memset(xp + i, 0, sz);
5144 memset(yp + i, 0, sz);
5145 memset(zp + i, 0, sz);
5147 if (n < mp->nframe_cells && mp->frame_cells != NULL) {
5148 Vector *cp = &(mp->frame_cells[n * 4]);
5149 dcd.globalcell[0] = VecLength(cp[0]);
5150 dcd.globalcell[2] = VecLength(cp[1]);
5151 dcd.globalcell[5] = VecLength(cp[2]);
5152 dcd.globalcell[1] = VecDot(cp[0], cp[1]) / (dcd.globalcell[0] * dcd.globalcell[2]);
5153 dcd.globalcell[3] = VecDot(cp[0], cp[2]) / (dcd.globalcell[0] * dcd.globalcell[5]);
5154 dcd.globalcell[4] = VecDot(cp[1], cp[2]) / (dcd.globalcell[2] * dcd.globalcell[5]);
5156 if (DcdWriteFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
5157 s_append_asprintf(errbuf, "Write error in dcd file");
5173 MoleculeWriteExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
5179 fp = fopen(fname, "wb");
5181 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
5184 if (mp->cell != NULL) {
5185 fprintf(fp, "Bounding box:\n");
5186 for (i = 0; i < 3; i++) {
5187 v = mp->cell->axes[i];
5188 fprintf(fp, "%.3f %.3f %.3f %d\n", v.x, v.y, v.z, mp->cell->flags[i]);
5190 fprintf(fp, "Bounding box origin:\n");
5191 v = mp->cell->origin;
5192 fprintf(fp, "%.3f %.3f %.3f\n", v.x, v.y, v.z);
5199 sCompareByElement(const void *ap, const void *bp)
5201 return ((*(Atom **)bp)->atomicNumber - (*(Atom **)ap)->atomicNumber);
5205 sMakeAdc(int n, int base, Symop symop)
5208 if (SYMOP_ALIVE(symop)) {
5210 sym = (symop.dx + 5) * 10000 + (symop.dy + 5) * 1000 + (symop.dz + 5) * 100 + symop.sym + 1;
5215 return (an + 1) * 100000 + sym;
5219 sCompareAdc(const void *ap, const void *bp)
5221 int n = *((Int *)ap) % 100000 - *((Int *)bp) % 100000;
5223 n = *((Int *)ap) / 100000 - *((Int *)bp) / 100000;
5228 sOutputAtomListInstructions(FILE *fp, int natoms, Atom *atoms)
5230 int i, j, k, an, sym;
5233 adc = (Int *)malloc(sizeof(Int) * natoms);
5236 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
5237 if (ap->exflags & kAtomHiddenFlag)
5239 adc[i] = sMakeAdc(i, ap->symbase, ap->symop);
5241 mergesort(adc, natoms, sizeof(Int), sCompareAdc);
5243 /* Create the atom list */
5245 for (i = j = k = 0; i < natoms; i++) {
5246 int an1 = adc[i] / 100000;
5247 int sym1 = adc[i] % 100000;
5248 if (sym == sym1 && an1 == an + 1) {
5255 /* Output the last atom with a minus sign */
5256 adc[j++] = -(an * 100000 + sym);
5257 /* Output this atom */
5264 adc[j++] = -(an * 100000 + sym);
5266 /* Create the instruction cards */
5267 for (i = k = 0; i < j; i++) {
5269 fprintf(fp, " 401");
5270 fprintf(fp, "%9d", adc[i]);
5272 if (i == j - 1 || k == 6 || (k == 5 && i < j - 2 && adc[i + 2] < 0)) {
5281 sEllipsoidType(int an)
5283 return (an >= 18 ? 3 : (an >= 2 && an != 6 ? 2 : (an > 0 ? 1 : 0)));
5287 sOutputAtomTypeInstructions(FILE *fp, int natoms, Atom *atoms)
5291 int etype, elast, istart, ilast, n1, n2;
5292 elast = istart = ilast = -1;
5293 for (i = 0, ap = atoms; i <= natoms; i++, ap++) {
5295 if (SYMOP_ALIVE(ap->symop))
5297 if (ap->exflags & kAtomHiddenFlag)
5299 etype = sEllipsoidType(ap->atomicNumber);
5304 } else if (elast == etype && ilast == i - 1) {
5309 /* Output the instruction card for the 'last' block of atoms */
5312 n1 = 4; n2 = 0; break;
5314 n1 = 4; n2 = 5; break;
5316 n1 = 1; n2 = 0; break;
5318 fprintf(fp, " 1 715 %8d 0 %8d 0 0.100 0.000 0.000\n", n1, n2);
5319 fprintf(fp, " %9d%9d\n", istart + 1, ilast + 1);
5326 sCompareBondType(const void *ap, const void *bp)
5328 /* Descending order */
5329 return *((int *)bp) - *((int *)ap);
5333 sOutputBondInstructions(FILE *fp, int natoms, Atom *atoms, int overlap_correction)
5337 int i, j, n[5], an, count, n1, n2, k;
5341 static const float sBondRad[4] = {0.060, 0.060, 0.060, 0.040};
5342 static const int sBondShade[4] = {5, 3, 1, 1};
5344 n[0] = n[1] = n[2] = n[3] = 0; /* Start index of 3rd row atoms (and higher), 2nd row, 1st row, and H */
5346 for (i = natoms - 1, ap = atoms + i; i >= 0; i--, ap--) {
5347 an = ap->atomicNumber;
5359 if (overlap_correction)
5360 strcpy(buf, " 2 1001 0.000\n");
5362 strcpy(buf, " 2 812\n");
5364 for (i = 0; i < 4; i++) {
5365 for (j = i; j < 4; j++) {
5366 /* Examine bonds between "group i" and "group j" */
5369 double min_bond = 10000.0; /* Minimum distance between bound atoms */
5370 double min_nonbond = 10000.0; /* Minimum distance between non-bound atoms */
5371 double max_bond = -10000.0; /* Maximum distance between bound atoms */
5372 int count_exbond = 0; /* Number of explicit bonds in this group */
5373 for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
5374 for (n2 = n[j], ap2 = atoms + n2; n2 < n[j + 1]; n2++, ap2++) {
5377 VecSub(dr, ap->r, ap2->r);
5379 cp = AtomConnectData(&ap->connect);
5380 for (k = ap->connect.count - 1; k >= 0; k--) {
5385 /* n1 and n2 are bound */
5391 /* n1 and n2 are not bound */
5392 if (d < min_nonbond)
5397 if (min_bond == 10000.0)
5398 continue; /* No bonds between these groups */
5400 if (max_bond + 0.002 < min_nonbond)
5403 max_bond = min_nonbond - 0.002;
5404 /* Some bonds may be omitted, so scan all bonds again */
5405 for (n1 = n[i], ap = ATOM_AT_INDEX(atoms, n1); n1 < n[i + 1]; n1++, ap = ATOM_NEXT(ap)) {
5406 cp = AtomConnectData(&ap->connect);
5407 for (k = ap->connect.count - 1; k >= 0; k--) {
5409 if (n2 < n[j] || n2 >= n[j + 1])
5412 VecSub(dr, ap->r, ap2->r);
5415 /* This bond should be explicitly defined */
5417 if (count_exbond == 0) {
5418 adc1 = -(i + 1); /* Bond type */
5419 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
5421 adc1 = sMakeAdc(n1, ap->symbase, ap->symop);
5422 adc2 = sMakeAdc(n2, ap2->symbase, ap2->symop);
5423 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
5424 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc2);
5430 /* Output the last instruction card */
5432 /* Make a new trailer card */
5433 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]);
5438 /* Output the last trailer card */
5443 if (count == 0 && overlap_correction) {
5444 /* 1001 card is not yet written, so write it */
5448 snprintf(buf, sizeof(buf), " 1 %3d", (overlap_correction ? 821 : 811));
5449 k = -exbonds[0] - 1; /* Bond type for the first block */
5450 i = 1; /* Index for exbonds[] */
5451 j = 0; /* Count in this block */
5452 while (i <= nexbonds) {
5453 if (j >= 29 || i == nexbonds || exbonds[i] < 0) {
5457 /* The trailer card */
5458 fprintf(fp, " %3d %6.3f\n", sBondShade[k], sBondRad[k]);
5462 k = -exbonds[i++] - 1; /* The new bond type */
5464 } else if (j > 0 && j % 3 == 0) {
5470 snprintf(buf + 9 + (j % 3) * 18, sizeof(buf) - 9 - (j % 3) * 18, "%9d%9d\n", n1, n2);
5478 MoleculeWriteToTepFile(Molecule *mp, const char *fname, char **errbuf)
5481 int i, j, natoms, *ip;
5483 Atom *ap, *atoms, **app;
5485 static Double sUnit[] = {1, 1, 1, 90, 90, 90};
5489 /* Create sorted array of atoms */
5490 natoms = mp->natoms;
5491 atoms = (Atom *)calloc(sizeof(Atom), natoms);
5492 app = (Atom **)calloc(sizeof(Atom *), natoms);
5493 ip = (int *)calloc(sizeof(int), natoms);
5494 if (atoms == NULL || app == NULL || ip == NULL) {
5495 s_append_asprintf(errbuf, "Cannot allocate memory");
5498 /* Sort the atom pointer by atomic number */
5499 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
5501 mergesort(app, natoms, sizeof(Atom *), sCompareByElement);
5502 for (i = 0; i < natoms; i++) {
5503 /* ip[old_index] is new_index */
5504 ip[app[i] - mp->atoms] = i;
5506 /* Copy the atom record to atoms[] */
5507 /* The 'v' member contains crystallographic coordinates */
5508 /* The connection table and symbase are renumbered */
5509 /* Hidden flags are modified to reflect the visibility in the MainView */
5510 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
5511 AtomDuplicateNoFrame(ap, app[i]);
5512 /* memmove(ap, app[i], gSizeOfAtomRecord); */
5513 MoleculeCartesianToXtal(mp, &(ap->v), &(ap->r));
5514 cp = AtomConnectData(&ap->connect);
5515 for (j = ap->connect.count - 1; j >= 0; j--) {
5518 if (SYMOP_ALIVE(ap->symop))
5519 ap->symbase = ip[ap->symbase];
5520 if (MainView_isAtomHidden(mp->mview, i)) {
5521 ap->exflags |= kAtomHiddenFlag;
5523 ap->exflags &= ~kAtomHiddenFlag;
5529 fp = fopen(fname, "wb");
5531 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
5536 fprintf(fp, "Generated by Molby\n");
5539 if (mp->cell != NULL) {
5540 dp = mp->cell->cell;
5544 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]);
5546 /* Symmetry operations */
5547 if (mp->nsyms > 0) {
5548 for (i = 0; i < mp->nsyms; i++) {
5550 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]);
5553 fprintf(fp, "1 0 1 0 0 0 0 1 0 0 0 0 1\n");
5557 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
5558 /* The 'v' field contains crystallographic coordinates */
5559 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);
5560 if (ap->aniso != NULL) {
5561 dp = ap->aniso->bij;
5562 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);
5564 Double temp = ap->tempFactor;
5567 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", temp, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5570 /* Special points */
5572 Vector camera, lookat, up, xvec, yvec, zvec;
5573 MainView_getCamera(mp->mview, &camera, &lookat, &up);
5574 VecSub(zvec, lookat, camera);
5575 VecCross(xvec, zvec, up);
5576 NormalizeVec(&xvec, &xvec);
5577 NormalizeVec(&yvec, &up);
5578 VecInc(xvec, lookat);
5579 VecInc(yvec, lookat);
5580 MoleculeCartesianToXtal(mp, &lookat, &lookat);
5581 MoleculeCartesianToXtal(mp, &xvec, &xvec);
5582 MoleculeCartesianToXtal(mp, &yvec, &yvec);
5583 fprintf(fp, " ORGN %9g%9g%9g 0\n", 0.0, 0.0, 0.0);
5584 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5585 fprintf(fp, " CNTR %9g%9g%9g 0\n", lookat.x, lookat.y, lookat.z);
5586 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5587 fprintf(fp, " X %9g%9g%9g 0\n", xvec.x, xvec.y, xvec.z);
5588 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5589 fprintf(fp, " Y %9g%9g%9g 0\n", yvec.x, yvec.y, yvec.z);
5590 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);
5594 fprintf(fp, " 201\n");
5595 fprintf(fp, " 205 12\n");
5596 fprintf(fp, " 301 6.6 6.6 0 0.8\n");
5597 sOutputAtomListInstructions(fp, natoms, atoms);
5598 fprintf(fp, " 501%4d55501%4d55501%4d55501%4d55501%4d55501 1\n", natoms + 2, natoms + 2, natoms + 3, natoms + 2, natoms + 4);
5599 fprintf(fp, " 502 1 0.0 2 0.0 3 0.0\n");
5600 fprintf(fp, " 604 1.538\n");
5602 sOutputBondInstructions(fp, natoms, atoms, 1);
5603 sOutputAtomTypeInstructions(fp, natoms, atoms);
5604 sOutputBondInstructions(fp, natoms, atoms, 0);
5606 for (i = 0; i < natoms; i++) {
5607 AtomClean(atoms + i);
5611 fprintf(fp, " 202\n");
5612 fprintf(fp, " 0 -1\n");
5618 MoleculeDump(Molecule *mol)
5623 for (i = 0; i < mol->natoms; i++) {
5625 ap = ATOM_AT_INDEX(mol->atoms, i);
5626 snprintf(buf1, sizeof buf1, "%3.4s.%d", ap->resName, ap->resSeq);
5627 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);
5628 cp = AtomConnectData(&ap->connect);
5629 for (j = 0; j < ap->connect.count; j++) {
5630 fprintf(stderr, "%s%d", (j > 0 ? "," : ""), cp[j]);
5632 fprintf(stderr, "]\n");
5636 #pragma mark ====== MD support (including modification of Molecule) ======
5638 /* Call md_prepare for the MDArena. If MDArena has not been created, a new arena is created.
5639 If something goes wrong, returns 1 (for missing parameters) or -1 (more serious error).
5640 If retmsg is not NULL, a message describing the problem is returned there. This message
5641 must be free'd by the caller. */
5643 MoleculePrepareMDArena(Molecule *mol, int check_only, char **retmsg)
5646 Int nangles, *angles, ndihedrals, *dihedrals, nimpropers, *impropers;
5648 IntGroup *ig1, *ig2, *ig3;
5649 MDArena *arena = mol->arena;
5651 if (arena == NULL) {
5654 } else if (arena->xmol != mol)
5655 md_arena_set_molecule(arena, mol);
5657 arena->is_initialized = 0;
5659 /* Rebuild the tables */
5660 ig1 = ig2 = ig3 = NULL;
5661 nangles = MoleculeFindMissingAngles(mol, &angles);
5662 ndihedrals = MoleculeFindMissingDihedrals(mol, &dihedrals);
5663 nimpropers = MoleculeFindMissingImpropers(mol, &impropers);
5665 ig1 = IntGroupNewWithPoints(mol->nangles, nangles, -1);
5666 MolActionCreateAndPerform(mol, gMolActionAddAngles, nangles * 3, angles, ig1);
5668 IntGroupRelease(ig1);
5670 if (ndihedrals > 0) {
5671 ig2 = IntGroupNewWithPoints(mol->ndihedrals, ndihedrals, -1);
5672 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, ndihedrals * 4, dihedrals, ig2);
5674 IntGroupRelease(ig2);
5676 if (nimpropers > 0) {
5677 ig3 = IntGroupNewWithPoints(mol->nimpropers, nimpropers, -1);
5678 MolActionCreateAndPerform(mol, gMolActionAddImpropers, nimpropers * 4, impropers, ig3);
5680 IntGroupRelease(ig3);
5684 /* Update the path information of the molecule before MD setup */
5685 char *buf = (char *)malloc(4096);
5686 MoleculeCallback_pathName(mol, buf, sizeof buf);
5687 MoleculeSetPath(mol, buf);
5691 /* Prepare parameters and internal information */
5692 msg = md_prepare(arena, check_only);
5694 /* Some parameters are missing? */
5696 if (strstr(msg, "parameter") != NULL && strstr(msg, "missing") != NULL)
5700 asprintf(retmsg, "cannot initialize for MD: %s", msg);
5705 /* The local parameter list is updated */
5708 if (mol->par == NULL)
5709 mol->par = ParameterNew();
5710 for (parType = kFirstParType; parType <= kLastParType; parType++) {
5711 /* Delete global and undefined parameters */
5712 UnionPar *up, *upbuf;
5714 ig1 = IntGroupNew();
5715 for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx)) != NULL; idx++) {
5716 if (up->bond.src != 0)
5717 IntGroupAdd(ig1, idx, 1);
5719 if (IntGroupGetCount(ig1) > 0)
5720 MolActionCreateAndPerform(mol, gMolActionDeleteParameters, parType, ig1);
5721 IntGroupRelease(ig1);
5722 /* Copy global and undefined parameters from arena and insert to mol->par */
5723 nparams = ParameterGetCountForType(arena->par, parType);
5726 upbuf = (UnionPar *)calloc(sizeof(UnionPar), nparams);
5727 ig1 = IntGroupNew();
5728 ig2 = IntGroupNew();
5729 for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(arena->par, parType, idx)) != NULL; idx++) {
5730 if (up->bond.src > 0)
5731 IntGroupAdd(ig1, idx, 1); /* Global parameter */
5732 else if (up->bond.src < 0)
5733 IntGroupAdd(ig2, idx, 1); /* Undefined parameter */
5735 if ((count = IntGroupGetCount(ig1)) > 0) {
5736 /* Insert global parameters (at the top) */
5737 ParameterCopy(arena->par, parType, upbuf, ig1);
5738 ig3 = IntGroupNewWithPoints(0, count, -1);
5739 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5740 IntGroupRelease(ig3);
5742 if ((count = IntGroupGetCount(ig2)) > 0) {
5743 /* Insert undefined parameters (at the bottom) */
5744 ParameterCopy(arena->par, parType, upbuf, ig2);
5745 idx = ParameterGetCountForType(mol->par, parType);
5746 ig3 = IntGroupNewWithPoints(idx, count, -1);
5747 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5748 IntGroupRelease(ig3);
5750 IntGroupRelease(ig2);
5751 IntGroupRelease(ig1);
5754 mol->needsMDRebuild = 0; /* We know the "modified" parameters are consistent with the MDArena */
5759 *retmsg = strdup(msg);
5764 #pragma mark ====== Serialize ======
5767 MoleculeDeserialize(const char *data, Int length, Int *timep)
5777 par = ParameterNew();
5781 while (length >= 12) {
5782 const char *ptr = data + 8 + sizeof(Int);
5783 int len = *((const Int *)(data + 8));
5785 if (strcmp(data, "ATOM") == 0) {
5786 n = len / gSizeOfAtomRecord;
5787 NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, n);
5788 memmove(mp->atoms, ptr, len);
5789 } else if (strcmp(data, "ANISO") == 0) {
5790 n = len / (sizeof(Int) + sizeof(Aniso));
5791 for (i = 0; i < n; i++) {
5792 j = *((const Int *)ptr);
5793 if (j < 0 || j >= mp->natoms)
5795 ap = ATOM_AT_INDEX(mp->atoms, j);
5796 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
5797 if (ap->aniso == NULL)
5799 *(ap->aniso) = *((Aniso *)(ptr + sizeof(Int)));
5800 ptr += sizeof(Int) + sizeof(Aniso);
5802 } else if (strcmp(data, "FRAME") == 0) {
5803 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5804 if (ap->nframes == 0)
5809 NewArray(&ap->frames, &ap->nframes, sizeof(Vector), n);
5810 if (ap->frames == NULL)
5812 memmove(ap->frames, ptr, sizeof(Vector) * ap->nframes);
5813 ptr += sizeof(Vector) * ap->nframes;
5815 } else if (strcmp(data, "EXTCON") == 0) {
5816 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5817 if (ap->connect.count <= ATOM_CONNECT_LIMIT)
5819 n = ap->connect.count;
5820 ap->connect.count = 0;
5821 ap->connect.u.ptr = NULL;
5822 NewArray(&(ap->connect.u.ptr), &(ap->connect.count), sizeof(Int), n);
5823 memmove(ap->connect.u.ptr, ptr, sizeof(Int) * n);
5824 ptr += sizeof(Int) * n;
5826 } else if (strcmp(data, "BOND") == 0) {
5827 n = len / (sizeof(Int) * 2);
5828 NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, n);
5829 memmove(mp->bonds, ptr, len);
5830 } else if (strcmp(data, "ANGLE") == 0) {
5831 n = len / (sizeof(Int) * 3);
5832 NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, n);
5833 memmove(mp->angles, ptr, len);
5834 } else if (strcmp(data, "DIHED") == 0) {
5835 n = len / (sizeof(Int) * 4);
5836 NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, n);
5837 memmove(mp->dihedrals, ptr, len);
5838 } else if (strcmp(data, "IMPROP") == 0) {
5839 n = len / (sizeof(Int) * 4);
5840 NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, n);
5841 memmove(mp->impropers, ptr, len);
5842 } else if (strcmp(data, "RESIDUE") == 0) {
5844 NewArray(&mp->residues, &mp->nresidues, 4, n);
5845 memmove(mp->residues, ptr, len);
5846 } else if (strcmp(data, "CELL") == 0) {
5847 mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
5848 if (mp->cell == NULL)
5850 memmove(mp->cell, ptr, sizeof(XtalCell));
5851 } else if (strcmp(data, "SYMOP") == 0) {
5852 n = len / sizeof(Transform);
5853 NewArray(&mp->syms, &mp->nsyms, sizeof(Transform), n);
5854 memmove(mp->syms, ptr, len);
5855 } else if (strcmp(data, "ANCHOR") == 0) {
5856 const char *ptr2 = ptr + len;
5857 while (ptr < ptr2) {
5859 memset(&an, 0, sizeof(an));
5861 if (i >= 0 && i < mp->natoms) {
5862 n = *((Int *)(ptr + sizeof(Int)));
5863 AtomConnectResize(&(an.connect), n);
5864 memmove(AtomConnectData(&(an.connect)), ptr + sizeof(Int) * 2, sizeof(Int) * n);
5865 NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), n);
5866 memmove(an.coeffs, ptr + sizeof(Int) * (2 + n), sizeof(Double) * n);
5867 ap = ATOM_AT_INDEX(mp->atoms, i);
5868 ap->anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
5869 memmove(ap->anchor, &an, sizeof(PiAnchor));
5871 ptr += sizeof(Int) * (2 + n) + sizeof(Double) * n;
5873 } else if (strcmp(data, "TIME") == 0) {
5875 *timep = *((Int *)ptr);
5876 } else if (strcmp(data, "BONDPAR") == 0) {
5878 n = len / sizeof(BondPar);
5879 NewArray(&par->bondPars, &par->nbondPars, sizeof(BondPar), n);
5880 memmove(par->bondPars, ptr, len);
5881 } else if (strcmp(data, "ANGPAR") == 0) {
5883 n = len / sizeof(AnglePar);
5884 NewArray(&par->anglePars, &par->nanglePars, sizeof(AnglePar), n);
5885 memmove(par->anglePars, ptr, len);
5886 } else if (strcmp(data, "DIHEPAR") == 0) {
5888 n = len / sizeof(TorsionPar);
5889 NewArray(&par->dihedralPars, &par->ndihedralPars, sizeof(TorsionPar), n);
5890 memmove(par->dihedralPars, ptr, len);
5891 } else if (strcmp(data, "IMPRPAR") == 0) {
5893 n = len / sizeof(TorsionPar);
5894 NewArray(&par->improperPars, &par->nimproperPars, sizeof(TorsionPar), n);
5895 memmove(par->improperPars, ptr, len);
5896 } else if (strcmp(data, "VDWPAR") == 0) {
5898 n = len / sizeof(VdwPar);
5899 NewArray(&par->vdwPars, &par->nvdwPars, sizeof(VdwPar), n);
5900 memmove(par->vdwPars, ptr, len);
5901 } else if (strcmp(data, "VDWPPAR") == 0) {
5903 n = len / sizeof(VdwPairPar);
5904 NewArray(&par->vdwpPars, &par->nvdwpPars, sizeof(VdwPairPar), n);
5905 memmove(par->vdwpPars, ptr, len);
5906 } else if (strcmp(data, "VCUTPAR") == 0) {
5908 n = len / sizeof(VdwCutoffPar);
5909 NewArray(&par->vdwCutoffPars, &par->nvdwCutoffPars, sizeof(VdwCutoffPar), n);
5910 memmove(par->vdwCutoffPars, ptr, len);
5912 len += 8 + sizeof(Int);
5916 if (mp->par == NULL)
5917 ParameterRelease(par);
5918 /* result = MoleculeRebuildTablesFromConnects(mp);
5924 Panic("Low memory while deserializing molecule data");
5925 return NULL; /* Not reached */
5928 Panic("internal error: bad format during deserializing molecule data");
5929 return NULL; /* Not reached */
5933 MoleculeSerialize(Molecule *mp, Int *outLength, Int *timep)
5936 int len, len_all, i, naniso, nframes, nconnects, nanchors;
5939 /* Array of atoms */
5940 len = 8 + sizeof(Int) + gSizeOfAtomRecord * mp->natoms;
5941 ptr = (char *)malloc(len);
5944 memmove(ptr, "ATOM\0\0\0\0", 8);
5945 *((Int *)(ptr + 8)) = gSizeOfAtomRecord * mp->natoms;
5946 p = ptr + 8 + sizeof(Int);
5947 memmove(p, mp->atoms, gSizeOfAtomRecord * mp->natoms);
5948 naniso = nframes = nconnects = nanchors = 0;
5949 for (i = 0; i < mp->natoms; i++) {
5950 ap = ATOM_AT_INDEX(p, i);
5951 if (ap->aniso != NULL) {
5955 if (ap->frames != NULL) {
5956 nframes += ap->nframes;
5959 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5960 nconnects += ap->connect.count;
5961 ap->connect.u.ptr = NULL;
5963 if (ap->anchor != NULL) {
5970 /* Array of aniso */
5972 len = 8 + sizeof(Int) + (sizeof(Int) + sizeof(Aniso)) * naniso;
5973 ptr = (char *)realloc(ptr, len_all + len);
5977 memmove(p, "ANISO\0\0\0", 8);
5978 *((Int *)(p + 8)) = (sizeof(Int) + sizeof(Aniso)) * naniso;
5979 p += 8 + sizeof(Int);
5980 for (i = 0; i < mp->natoms; i++) {
5981 ap = ATOM_AT_INDEX(mp->atoms, i);
5982 if (ap->aniso != NULL) {
5984 *((Aniso *)(p + sizeof(Int))) = *(ap->aniso);
5985 p += sizeof(Int) + sizeof(Aniso);
5991 /* Array of frames */
5993 len = 8 + sizeof(Int) + sizeof(Vector) * nframes;
5994 ptr = (char *)realloc(ptr, len_all + len);
5998 memmove(p, "FRAME\0\0\0", 8);
5999 *((Int *)(p + 8)) = sizeof(Vector) * nframes;
6000 p += 8 + sizeof(Int);
6001 for (i = 0; i < mp->natoms; i++) {
6002 ap = ATOM_AT_INDEX(mp->atoms, i);
6003 if (ap->frames != NULL) {
6004 memmove(p, ap->frames, sizeof(Vector) * ap->nframes);
6005 p += sizeof(Vector) * ap->nframes;
6011 /* Array of connects */
6012 if (nconnects > 0) {
6013 len = 8 + sizeof(Int) + sizeof(Int) * nconnects;
6014 ptr = (char *)realloc(ptr, len_all + len);
6018 memmove(p, "EXTCON\0\0", 8);
6019 *((Int *)(p + 8)) = sizeof(Int) * nconnects;
6020 p += 8 + sizeof(Int);
6021 for (i = 0; i < mp->natoms; i++) {
6022 ap = ATOM_AT_INDEX(mp->atoms, i);
6023 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
6024 memmove(p, ap->connect.u.ptr, sizeof(Int) * ap->connect.count);
6025 p += sizeof(Int) * ap->connect.count;
6031 /* Bonds, angles, dihedrals, impropers */
6032 if (mp->nbonds > 0) {
6033 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nbonds;
6034 ptr = (char *)realloc(ptr, len_all + len);
6038 memmove(p, "BOND\0\0\0\0", 8);
6039 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nbonds;
6040 p += 8 + sizeof(Int);
6041 memmove(p, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
6044 if (mp->nangles > 0) {
6045 len = 8 + sizeof(Int) + sizeof(Int) * 3 * mp->nangles;
6046 ptr = (char *)realloc(ptr, len_all + len);
6050 memmove(p, "ANGLE\0\0\0", 8);
6051 *((Int *)(p + 8)) = sizeof(Int) * 3 * mp->nangles;
6052 p += 8 + sizeof(Int);
6053 memmove(p, mp->angles, sizeof(Int) * 3 * mp->nangles);
6056 if (mp->ndihedrals > 0) {
6057 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->ndihedrals;
6058 ptr = (char *)realloc(ptr, len_all + len);
6062 memmove(p, "DIHED\0\0\0", 8);
6063 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->ndihedrals;
6064 p += 8 + sizeof(Int);
6065 memmove(p, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
6068 if (mp->nimpropers > 0) {
6069 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->nimpropers;
6070 ptr = (char *)realloc(ptr, len_all + len);
6074 memmove(p, "IMPROP\0\0", 8);
6075 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->nimpropers;
6076 p += 8 + sizeof(Int);
6077 memmove(p, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
6081 /* Array of residues */
6082 if (mp->nresidues > 0) {
6083 len = 8 + sizeof(Int) + 4 * mp->nresidues;
6084 ptr = (char *)realloc(ptr, len_all + len);
6088 memmove(p, "RESIDUE\0", 8);
6089 *((Int *)(p + 8)) = 4 * mp->nresidues;
6090 p += 8 + sizeof(Int);
6091 memmove(p, mp->residues, 4 * mp->nresidues);
6096 if (mp->cell != NULL) {
6097 len = 8 + sizeof(Int) + sizeof(XtalCell);
6098 ptr = (char *)realloc(ptr, len_all + len);
6102 memmove(p, "CELL\0\0\0\0", 8);
6103 *((Int *)(p + 8)) = sizeof(XtalCell);
6104 p += 8 + sizeof(Int);
6105 memmove(p, mp->cell, sizeof(XtalCell));
6109 /* Symmetry operations */
6110 if (mp->nsyms > 0) {
6111 len = 8 + sizeof(Int) + sizeof(Transform) * mp->nsyms;
6112 ptr = (char *)realloc(ptr, len_all + len);
6116 memmove(p, "SYMOP\0\0\0", 8);
6117 *((Int *)(p + 8)) = sizeof(Transform) * mp->nsyms;
6118 p += 8 + sizeof(Int);
6119 memmove(p, mp->syms, sizeof(Transform) * mp->nsyms);
6125 /* Estimate the necessary storage first */
6126 /* One entry consists of { atom_index (Int), number_of_connects (Int), connects (Int's), weights (Double's) } */
6127 len = 8 + sizeof(Int);
6128 for (i = 0; i < mp->natoms; i++) {
6129 ap = ATOM_AT_INDEX(mp->atoms, i);
6130 if (ap->anchor != NULL)
6131 len += sizeof(Int) * 2 + (sizeof(Int) + sizeof(Double)) * ap->anchor->connect.count;
6133 ptr = (char *)realloc(ptr, len_all + len);
6137 memmove(p, "ANCHOR\0\0", 8);
6138 *((Int *)(p + 8)) = len - (8 + sizeof(Int));
6139 p += 8 + sizeof(Int);
6140 for (i = 0; i < mp->natoms; i++) {
6142 ap = ATOM_AT_INDEX(mp->atoms, i);
6143 if (ap->anchor != NULL) {
6144 count = ap->anchor->connect.count;
6146 *((Int *)(p + sizeof(Int))) = count;
6147 p += sizeof(Int) * 2;
6148 ip = AtomConnectData(&(ap->anchor->connect));
6149 memmove(p, ip, sizeof(Int) * count);
6150 p += sizeof(Int) * count;
6151 memmove(p, ap->anchor->coeffs, sizeof(Double) * count);
6152 p += sizeof(Double) * count;
6159 if (mp->par != NULL) {
6161 for (type = kFirstParType; type <= kLastParType; type++) {
6162 const char *parname;
6163 Int parsize, parcount;
6167 parname = "BONDPAR\0";
6168 parsize = sizeof(BondPar);
6169 parcount = mp->par->nbondPars;
6170 parptr = mp->par->bondPars;
6173 parname = "ANGPAR\0\0";
6174 parsize = sizeof(AnglePar);
6175 parcount = mp->par->nanglePars;
6176 parptr = mp->par->anglePars;
6178 case kDihedralParType:
6179 parname = "DIHEPAR\0";
6180 parsize = sizeof(TorsionPar);
6181 parcount = mp->par->ndihedralPars;
6182 parptr = mp->par->dihedralPars;
6184 case kImproperParType:
6185 parname = "IMPRPAR\0";
6186 parsize = sizeof(TorsionPar);
6187 parcount = mp->par->nimproperPars;
6188 parptr = mp->par->improperPars;
6191 parname = "VDWPAR\0\0";
6192 parsize = sizeof(VdwPar);
6193 parcount = mp->par->nvdwPars;
6194 parptr = mp->par->vdwPars;
6196 case kVdwPairParType:
6197 parname = "VDWPPAR\0";
6198 parsize = sizeof(VdwPairPar);
6199 parcount = mp->par->nvdwpPars;
6200 parptr = mp->par->vdwpPars;
6202 case kVdwCutoffParType:
6203 parname = "VCUTPAR\0";
6204 parsize = sizeof(VdwCutoffPar);
6205 parcount = mp->par->nvdwCutoffPars;
6206 parptr = mp->par->vdwCutoffPars;
6212 len = 8 + sizeof(Int) + parsize * parcount;
6213 ptr = (char *)realloc(ptr, len_all + len);
6217 memmove(p, parname, 8);
6218 *((Int *)(p + 8)) = parsize * parcount;
6219 p += 8 + sizeof(Int);
6220 memmove(p, parptr, parsize * parcount);
6228 time_t tm = time(NULL);
6229 len = 8 + sizeof(Int) + sizeof(Int);
6230 ptr = (char *)realloc(ptr, len_all + len);
6234 memmove(p, "TIME\0\0\0\0", 8);
6235 *((Int *)(p + 8)) = sizeof(Int);
6236 p += 8 + sizeof(Int);
6237 *((Int *)p) = (Int)tm;
6243 if (outLength != NULL)
6244 *outLength = len_all;
6248 Panic("Low memory while serializing a molecule data");
6249 return NULL; /* Not reached */
6252 #pragma mark ====== Search for bonds, angles, dihedrals, impropers ======
6255 sMoleculeSearchIncludingAtoms(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
6259 IntGroup *gp = NULL;
6260 if (atomgroup == NULL)
6262 for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
6263 for (j = 0; j < nsize; j++) {
6264 if (IntGroupLookup(atomgroup, ip[j], NULL) != 0) {
6267 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
6268 Panic("Low memory while searching %s", msg);
6277 MoleculeSearchBondsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6281 return sMoleculeSearchIncludingAtoms(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
6285 MoleculeSearchAnglesIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6289 return sMoleculeSearchIncludingAtoms(mp->nangles, mp->angles, 3, atomgroup, "angles");
6293 MoleculeSearchDihedralsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6297 return sMoleculeSearchIncludingAtoms(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
6301 MoleculeSearchImpropersIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6305 return sMoleculeSearchIncludingAtoms(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
6309 sMoleculeSearchAcrossAtomGroup(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
6313 IntGroup *gp = NULL;
6314 if (atomgroup == NULL)
6316 for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
6318 for (j = 0; j < nsize; j++) {
6320 kk = (IntGroupLookup(atomgroup, ip[j], NULL) != 0);
6324 /* This bond etc. crosses the atom group border */
6327 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
6328 Panic("Low memory while searching %s", msg);
6337 MoleculeSearchBondsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6341 return sMoleculeSearchAcrossAtomGroup(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
6345 MoleculeSearchAnglesAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6349 return sMoleculeSearchAcrossAtomGroup(mp->nangles, mp->angles, 3, atomgroup, "angles");
6353 MoleculeSearchDihedralsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6357 return sMoleculeSearchAcrossAtomGroup(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
6361 MoleculeSearchImpropersAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6365 return sMoleculeSearchAcrossAtomGroup(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
6368 /* Subroutine for MoleculeGuessBonds. It can be also used independently, but make sure that *outNbonds / *outBonds
6369 _correctly_ represents an array of two integers (as in mp->nbonds/mp->bonds). */
6370 /* Find atoms within the given "distance" from the given position. */
6371 /* If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
6372 the threshold distance is given by the sum of van der Waals radii times limit, and radius is
6373 the van der Waals radius of the atom at the given position. */
6374 /* Index is the atom index of the given atom; it is only used in returning the "bond" array
6375 to the caller. If index is negative, then (-index) is the real atom index, and
6376 only atoms with lower indices than (-index) are looked for. */
6378 MoleculeFindCloseAtoms(Molecule *mp, const Vector *vp, Double radius, Double limit, Int *outNbonds, Int **outBonds, Int index)
6380 Int n2, j, nlim, newbond[2];
6384 nlim = index = -index;
6388 for (j = 0; j < nlim; j++) {
6389 Atom *bp = ATOM_AT_INDEX(mp->atoms, j);
6392 n2 = bp->atomicNumber;
6393 if (n2 >= 0 && n2 < gCountElementParameters)
6394 a2 = gElementParameters[n2].radius;
6395 else a2 = gElementParameters[6].radius;
6397 VecSub(dr, *vp, r2);
6401 alim = limit * (radius + a2);
6402 if (VecLength2(dr) < alim * alim) {
6405 /* MoleculeAddBonds(mp, 1, newbonds); */
6406 AssignArray(outBonds, outNbonds, sizeof(Int) * 2, *outNbonds, newbond);
6412 /* Guess the bonds from the coordinates */
6413 /* If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
6414 the threshold distance is given by the sum of van der Waals radii times limit. */
6416 MoleculeGuessBonds(Molecule *mp, Double limit, Int *outNbonds, Int **outBonds)
6418 Int nbonds, *bonds, i, newbond[2];
6424 for (i = 1, ap = ATOM_NEXT(mp->atoms); i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6426 Int an = ap->atomicNumber;
6428 if (an >= 0 && an < gCountElementParameters)
6429 rad = gElementParameters[an].radius;
6430 else rad = gElementParameters[6].radius;
6431 MoleculeFindCloseAtoms(mp, &r, rad, limit, &nbonds, &bonds, -i);
6434 newbond[0] = kInvalidIndex;
6436 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
6439 if (outNbonds != NULL)
6440 *outNbonds = nbonds;
6441 if (outBonds != NULL)
6446 /* Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information */
6448 MoleculeRebuildTablesFromConnects(Molecule *mp)
6450 int i, j, k, retval;
6457 if (mp->nbonds == 0) {
6458 for (i = 0; i < mp->natoms; i++) {
6459 ap = ATOM_AT_INDEX(mp->atoms, i);
6460 cp = AtomConnectData(&ap->connect);
6461 for (j = 0; j < ap->connect.count; j++) {
6467 /* MoleculeAddBonds() should not be used, because it assumes connects[] and
6468 bonds are already in sync */
6469 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
6470 /* retval = MoleculeAddBonds(mp, 1, ibuf);
6478 if (mp->nangles == 0) {
6479 for (i = 0; i < mp->natoms; i++) {
6480 ap = ATOM_AT_INDEX(mp->atoms, i);
6481 cp = AtomConnectData(&ap->connect);
6482 for (j = 0; j < ap->connect.count; j++) {
6483 for (k = j + 1; k < ap->connect.count; k++) {
6488 retval = MoleculeAddAngles(mp, ibuf, NULL);
6496 /* Find dihedrals */
6497 if (mp->ndihedrals == 0) {
6498 for (i = 0; i < mp->natoms; i++) {
6499 ap = ATOM_AT_INDEX(mp->atoms, i);
6500 cp = AtomConnectData(&ap->connect);
6501 for (j = 0; j < ap->connect.count; j++) {
6508 apjj = ATOM_AT_INDEX(mp->atoms, jj);
6509 cpjj = AtomConnectData(&apjj->connect);
6510 for (k = 0; k < ap->connect.count; k++) {
6514 for (m = 0; m < apjj->connect.count; m++) {
6516 if (mm == i || mm == kk)
6523 retval = MoleculeAddDihedrals(mp, ibuf, NULL);
6532 /* Find impropers */
6533 if (mp->nimpropers == 0) {
6534 for (i = 0; i < mp->natoms; i++) {
6535 int i1, i2, i4, n1, n2, n4;
6536 ap = ATOM_AT_INDEX(mp->atoms, i);
6537 cp = AtomConnectData(&ap->connect);
6538 for (i1 = 0; i1 < ap->connect.count; i1++) {
6540 for (i2 = i1 + 1; i2 < ap->connect.count; i2++) {
6542 for (i4 = i2 + 1; i4 < ap->connect.count; i4++) {
6549 retval = MoleculeAddImpropers(mp, ibuf, NULL);
6558 mp->needsMDRebuild = 1;
6559 __MoleculeUnlock(mp);
6563 __MoleculeUnlock(mp);
6568 MoleculeAreAtomsConnected(Molecule *mol, int idx1, int idx2)
6570 Atom *ap1 = ATOM_AT_INDEX(mol->atoms, idx1);
6571 if (AtomConnectHasEntry(&ap1->connect, idx2))
6573 else if (ap1->anchor != NULL && AtomConnectHasEntry(&(ap1->anchor->connect), idx2))
6578 #pragma mark ====== Atom names ======
6580 /* Look for the n1-th atom in resno-th residue (n1 is 0-based) */
6582 MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
6586 if (mp == NULL || mp->natoms == 0)
6589 for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6590 if (ap->resSeq == resno) {
6597 return lasti; /* max */
6602 MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
6606 n = strtol(s, &p, 0);
6618 if ((p = strchr(s, ':')) != NULL) {
6619 /* Residue is specified */
6621 if ((pp = strchr(s, '.')) != NULL && pp < p) {
6622 /* Residue number is also specified */
6625 *resSeq = strtol(pp + 1, &ppp, 0);
6627 return -2; /* Bad format */
6628 while (isspace(*ppp))
6631 return -2; /* Bad format */
6634 /* Check whether the "residue name" is an integer */
6635 n = strtol(s, &pp, 0);
6637 while (isspace(*pp))
6639 if (*pp == 0 || *pp == ':') {
6642 return -2; /* Bad format */
6650 if (n >= sizeof(resName))
6651 n = sizeof(resName) - 1;
6652 strncpy(resName, s, n);
6660 strncpy(atomName, p, 4);
6665 /* Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer */
6667 MoleculeAtomIndexFromString(Molecule *mp, const char *s)
6674 n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
6675 if (atomName[0] == 0) {
6676 if (n >= mp->natoms)
6677 n = -1; /* Out of range */
6680 for (n = 0; n < mp->natoms; n++) {
6681 Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
6682 if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
6683 && (resSeq < 0 || ap->resSeq == resSeq)
6684 && strncmp(atomName, ap->aname, 4) == 0) {
6688 return -1; /* Not found */
6692 MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
6696 if (mp == NULL || index < 0 || index >= mp->natoms) {
6700 ap = mp->atoms + index;
6701 if (ap->resSeq != 0) {
6702 n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
6706 snprintf(buf, bufsize, "%.4s", ap->aname);
6709 #pragma mark ====== Selection ======
6712 sMoleculeNotifyChangeSelection(Molecule *mp)
6714 /* TODO: Finer control of notification types may be necessary */
6715 MoleculeCallback_notifyModification(mp, 0);
6719 MoleculeSetSelection(Molecule *mp, IntGroup *select)
6724 IntGroupRetain(select);
6725 if (mp->selection != NULL)
6726 IntGroupRelease(mp->selection);
6727 mp->selection = select;
6728 sMoleculeNotifyChangeSelection(mp);
6732 MoleculeGetSelection(Molecule *mp)
6736 else return mp->selection;
6740 MoleculeSelectAtom(Molecule *mp, int n1, int extending)
6742 if (mp->selection == NULL)
6743 mp->selection = IntGroupNew();
6745 IntGroupClear(mp->selection);
6746 IntGroupAdd(mp->selection, n1, 1);
6747 sMoleculeNotifyChangeSelection(mp);
6751 MoleculeUnselectAtom(Molecule *mp, int n1)
6753 if (mp->selection != NULL)
6754 IntGroupRemove(mp->selection, n1, 1);
6755 sMoleculeNotifyChangeSelection(mp);
6759 MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
6761 if (mp->selection == NULL)
6762 mp->selection = IntGroupNew();
6763 IntGroupReverse(mp->selection, n1, 1);
6764 sMoleculeNotifyChangeSelection(mp);
6768 MoleculeIsAtomSelected(Molecule *mp, int n1)
6770 if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
6776 MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
6778 if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
6784 MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
6787 IntGroup *remain, *ig1, *ig2;
6789 remain = IntGroupNewFromIntGroup(remove);
6793 status = IntGroupReverse(remain, 0, mp->natoms);
6795 ig1 = IntGroupNew();
6799 status = IntGroupDifference(selection, remove, ig1);
6802 ig2 = IntGroupNew();
6806 status = IntGroupDeconvolute(ig1, remain, ig2);
6809 IntGroupRelease(remain);
6811 IntGroupRelease(ig1);
6816 IntGroupRelease(ig2);
6821 #pragma mark ====== Atom Equivalence ======
6825 struct sEqList *next;
6826 struct sEqList *link;
6829 static struct sEqList *sListBase = NULL;
6830 static struct sEqList *sListFree = NULL;
6832 static struct sEqList *
6836 if (sListFree != NULL) {
6838 sListFree = lp->next;
6839 lp->i[0] = lp->i[1] = 0;
6843 lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
6844 lp->link = sListBase;
6850 sFreeEqList(struct sEqList *list)
6852 list->next = sListFree;
6857 sDeallocateEqLists(void)
6859 struct sEqList *lp, *lp_link;
6860 for (lp = sListBase; lp != NULL; lp = lp_link) {
6869 sExistInEqList(int i, int idx, struct sEqList *list)
6871 while (list != NULL) {
6872 if (list->i[idx] == i)
6879 static struct sEqList *
6880 sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
6883 struct sEqList *list1, *list2;
6884 Int ii, jj, ni, nj, *cpi, *cpj;
6885 api = ATOM_AT_INDEX(mol->atoms, i);
6886 apj = ATOM_AT_INDEX(mol->atoms, j);
6887 if (api->atomicNumber != apj->atomicNumber)
6889 list1 = sAllocEqList();
6895 if (i == j || (db[i] != NULL && db[i] == db[j]))
6897 cpi = AtomConnectData(&api->connect);
6898 cpj = AtomConnectData(&apj->connect);
6899 for (ni = 0; ni < api->connect.count; ni++) {
6901 if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
6903 if (sExistInEqList(ii, 0, list1))
6906 for (nj = 0; nj < apj->connect.count; nj++) {
6908 if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6910 if (sExistInEqList(jj, 1, list1))
6912 list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
6916 if (list2 == NULL) {
6918 return NULL; /* No equivalent to ii */
6920 list1 = list2; /* ii is OK, try next */
6926 sDBInclude(Int *ip, int i)
6931 for (j = ip[0] - 1; j >= 0; j--) {
6939 MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
6941 Int **db; /* List of equivalents for each atom */
6943 Atom *api, *apj, *apk;
6944 Int *cpi, *cpj, *ibuf, nibuf;
6945 int i, j, k, ii, jj, kk;
6946 if (mol == NULL || mol->natoms == 0)
6948 db = (Int **)calloc(sizeof(Int *), mol->natoms);
6952 /* Find the equivalent univalent atoms */
6953 for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6954 if (api->connect.count < 2)
6956 cpi = AtomConnectData(&api->connect);
6957 for (j = 0; j < api->connect.count; j++) {
6961 if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6963 AssignArray(&ibuf, &nibuf, sizeof(Int), n, &jj);
6965 apj = ATOM_AT_INDEX(mol->atoms, jj);
6966 if (apj->connect.count != 1 || db[jj] != NULL)
6968 cpj = AtomConnectData(&apj->connect);
6969 for (k = j + 1; k < api->connect.count; k++) {
6971 if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
6973 apk = ATOM_AT_INDEX(mol->atoms, kk);
6974 if (apk->connect.count != 1 || db[kk] != NULL)
6976 if (apj->atomicNumber == apk->atomicNumber) {
6977 AssignArray(&ibuf, &nibuf, sizeof(Int), n, &kk);
6982 ip = (Int *)calloc(sizeof(Int), n + 1);
6986 memmove(ip + 1, ibuf, sizeof(Int) * n);
6987 for (k = 0; k < n; k++)
6997 /* Try matching (i,j) pair */
6998 for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6999 if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
7001 for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
7002 struct sEqList *list;
7003 if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
7005 if (api->atomicNumber != apj->atomicNumber)
7006 continue; /* Different elements do not match */
7007 if (db[i] != NULL && db[i] == db[j])
7008 continue; /* Already equivalent */
7009 list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
7011 continue; /* (i,j) do not match */
7012 while (list != NULL) {
7015 if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
7016 /* Merge db[ii] and db[jj] */
7017 k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
7018 ip = (Int *)calloc(sizeof(Int), k + 1);
7020 return NULL; /* Out of memory */
7021 if (db[ii] == NULL) {
7025 memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
7028 if (db[jj] == NULL) {
7031 memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
7040 for (k = 0; k < ip[0]; k++)
7044 printf("(%d,%d) matched: ", ii, jj);
7045 for (k = 0; k < ip[0]; k++) {
7046 printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
7056 /* Record the equivalent atoms with the lowest index for each atom */
7057 result = (Int *)calloc(sizeof(Int), mol->natoms);
7058 for (i = 0; i < mol->natoms; i++)
7060 for (i = 0; i < mol->natoms; i++) {
7061 if (result[i] >= 0 || (ip = db[i]) == NULL)
7064 for (j = 0; j < ip[0]; j++) {
7069 for (j = 0; j < ip[0]; j++) {
7070 result[ip[j + 1]] = k;
7071 db[ip[j + 1]] = NULL;
7075 sDeallocateEqLists();
7079 #pragma mark ====== Symmetry expansion ======
7082 MoleculeGetTransformForSymop(Molecule *mp, Symop symop, Transform *tf, int is_cartesian)
7085 if (mp == NULL || mp->cell == NULL)
7087 if (symop.sym >= mp->nsyms && symop.sym != 0)
7089 memmove(*tf, SYMMETRY_AT_INDEX(mp->syms, symop.sym), sizeof(Transform));
7090 (*tf)[9] += symop.dx;
7091 (*tf)[10] += symop.dy;
7092 (*tf)[11] += symop.dz;
7094 TransformMul(t, *tf, mp->cell->rtr);
7095 TransformMul(*tf, mp->cell->tr, t);
7101 MoleculeGetSymopForTransform(Molecule *mp, const Transform tf, Symop *symop, int is_cartesian)
7105 if (mp == NULL || mp->cell == NULL)
7108 TransformMul(t, tf, mp->cell->tr);
7109 TransformMul(t, mp->cell->rtr, t);
7111 memmove(t, tf, sizeof(Transform));
7113 for (i = 0; i < mp->nsyms || i == 0; i++) {
7114 Transform *tp = &(SYMMETRY_AT_INDEX(mp->syms, i));
7115 for (j = 0; j < 9; j++) {
7116 if (fabs((*tp)[j] - t[j]) > 1e-4)
7120 for (j = 9; j < 12; j++) {
7121 double f1 = t[j] - (*tp)[j];
7122 double f2 = floor(f1 + 0.5);
7123 if (fabs(f1 - f2) > 1e-4)
7133 symop->alive = (SYMOP_ALIVE((*symop)) != 0);
7138 return -3; /* Not found */
7142 MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
7146 if (symop.sym >= mp->nsyms && symop.sym != 0)
7148 if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
7149 TransformVec(vpout, mp->cell->rtr, vpin);
7150 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpout);
7151 vpout->x += symop.dx;
7152 vpout->y += symop.dy;
7153 vpout->z += symop.dz;
7154 TransformVec(vpout, mp->cell->tr, vpout);
7156 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpin);
7157 vpout->x += symop.dx;
7158 vpout->y += symop.dy;
7159 vpout->z += symop.dz;
7164 /* Add expanded atoms. Returns the number of newly created atoms.
7165 If indices is non-NULL, it should be an array of Int with at least
7166 IntGroupGetCount(group) entries, and on return it contains the
7167 indices of the expanded atoms (may be existing atoms if the expanded
7168 atoms are already present)
7169 If allowOverlap is non-zero, then the new atom is created even when the
7170 coordinates coincide with the some other atom (special position) of the
7171 same element; otherwise, such atom will not be created and the existing
7172 atom is returned in indices[]. */
7174 MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group, Int *indices, Int allowOverlap)
7176 int i, n, n0, n1, n2, base, count, *table;
7178 IntGroupIterator iter;
7184 if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
7186 if (symop.sym != 0 && symop.sym >= mp->nsyms)
7189 /* Create atoms, with avoiding duplicates */
7190 n0 = n1 = mp->natoms;
7191 table = (int *)malloc(sizeof(int) * n0);
7194 for (i = 0; i < n0; i++)
7196 IntGroupIteratorInit(group, &iter);
7197 MoleculeGetTransformForSymop(mp, symop, &tr, 0);
7199 for (i = 0; i < count; i++) {
7200 n = IntGroupIteratorNext(&iter);
7201 ap = ATOM_AT_INDEX(mp->atoms, n);
7202 if (SYMOP_ALIVE(ap->symop)) {
7203 /* Calculate the cumulative symop */
7205 MoleculeGetTransformForSymop(mp, ap->symop, &t1, 0);
7206 TransformMul(tr2, tr, t1);
7207 if (MoleculeGetSymopForTransform(mp, tr2, &symop1, 0) != 0) {
7208 if (indices != NULL)
7210 continue; /* Skip this atom */
7218 /* Calculate the expande position */
7219 MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
7221 /* Is this expansion already present? */
7222 for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
7223 /* Symmetry operation and the base atom are the same */
7224 if (ap2->symbase == base && SYMOP_EQUAL(symop1, ap2->symop))
7226 /* Atomic number and the position are the same */
7227 if (ap2->atomicNumber == ap->atomicNumber && allowOverlap == 0) {
7228 VecSub(dr, ap2->r, nr);
7229 if (VecLength2(dr) < 1e-6)
7234 /* If yes, then skip it */
7235 if (indices != NULL)
7239 /* Create a new atom */
7241 AtomDuplicate(&newAtom, ap);
7242 MoleculeCreateAnAtom(mp, &newAtom, -1);
7243 AtomClean(&newAtom);
7244 ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
7246 ap2->symbase = base;
7247 ap2->symop = symop1;
7248 ap2->symop.alive = (symop1.dx != 0 || symop1.dy != 0 || symop1.dz != 0 || symop1.sym != 0);
7249 table[n] = n1; /* The index of the new atom */
7250 MoleculeSetAnisoBySymop(mp, n1); /* Recalculate anisotropic parameters according to symop */
7251 if (indices != NULL)
7256 IntGroupIteratorRelease(&iter);
7259 for (i = n0; i < n1; i++) {
7261 ap = ATOM_AT_INDEX(mp->atoms, i);
7262 if (SYMOP_ALIVE(ap->symop) && MoleculeGetTransformForSymop(mp, ap->symop, &tr, 1) == 0) {
7263 /* For each connected atom, look for the transformed atom */
7265 ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
7266 cp = AtomConnectData(&ap2->connect);
7267 n2 = ap2->connect.count;
7268 for (n = 0; n < n2; n++) {
7269 Atom *apn = ATOM_AT_INDEX(mp->atoms, cp[n]);
7271 TransformVec(&nr, tr, &nr);
7272 /* Look for the bonded atom transformed by ap->symop */
7273 for (j = 0, ap2 = mp->atoms; j < mp->natoms; j++, ap2 = ATOM_NEXT(ap2)) {
7274 if (ap2->symbase == cp[n] && SYMOP_EQUAL(ap->symop, ap2->symop))
7276 VecSub(dr, nr, ap2->r);
7277 if (ap2->atomicNumber == apn->atomicNumber && VecLength2(dr) < 1e-6)
7280 if (j < mp->natoms) {
7281 /* Bond i-j is created */
7284 if (MoleculeLookupBond(mp, b[0], b[1]) < 0)
7285 MoleculeAddBonds(mp, 1, b, NULL, 1);
7290 mp->needsMDRebuild = 1;
7291 __MoleculeUnlock(mp);
7293 return n1 - n0; /* The number of added atoms */
7296 /* Recalculate the coordinates of symmetry expanded atoms.
7297 (Also recalculate the positions of pi-anchor atoms)
7298 Returns the number of affected atoms.
7299 If group is non-NULL, only the expanded atoms whose base atoms are in the
7300 given group are considered.
7301 If groupout and vpout are non-NULL, the indices of the affected atoms
7302 and the original positions are returned (for undo operation).
7303 The pointers returned in *groupout and *vpout must be released and
7304 free()'ed by the caller */
7306 MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
7311 IntGroup *ig = NULL;
7314 if (mp == NULL || mp->natoms == 0)
7319 if (mp->nsyms != 0) {
7320 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7321 if (!SYMOP_ALIVE(ap->symop))
7323 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
7325 bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
7326 MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
7327 VecSub(dr, nr, ap->r);
7328 if (VecLength2(dr) < 1e-20)
7330 if (groupout != NULL) {
7333 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
7336 IntGroupAdd(ig, i, 1);
7342 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7344 if (ap->anchor == NULL)
7346 if (group != NULL) {
7347 if (IntGroupLookup(group, i, NULL) == 0) {
7348 n = ap->anchor->connect.count;
7349 ip = AtomConnectData(&(ap->anchor->connect));
7350 for (j = 0; j < n; j++) {
7351 if (IntGroupLookup(group, ip[j], NULL) != 0)
7355 continue; /* This pi-anchor should not be modified */
7359 MoleculeCalculatePiAnchorPosition(mp, i);
7360 VecSub(dr, nr, ap->r);
7361 if (VecLength2(dr) < 1e-20) {
7362 ap->r = nr; /* No change */
7365 if (groupout != NULL) {
7368 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
7371 IntGroupAdd(ig, i, 1);
7375 mp->needsMDCopyCoordinates = 1;
7376 __MoleculeUnlock(mp);
7379 if (groupout != NULL && vpout != NULL) {
7381 *vpout = (Vector *)realloc(vp, sizeof(Vector) * count);
7383 IntGroupRelease(ig);
7387 if (groupout != NULL && vpout != NULL) {
7395 #pragma mark ====== Show/hide atoms ======
7398 sMoleculeNotifyChangeAppearance(Molecule *mp)
7400 /* TODO: Finer control of notification types may be necessary */
7401 MoleculeCallback_notifyModification(mp, 0);
7406 sMoleculeUnselectHiddenAtoms(Molecule *mp)
7409 if (mp == NULL || mp->selection == NULL)
7411 for (i = 0; i < mp->natoms; i++) {
7412 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7413 if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
7414 IntGroupRemove(mp->selection, i, 1);
7416 sMoleculeNotifyChangeAppearance(mp);
7420 MoleculeShowAllAtoms(Molecule *mp)
7425 for (i = 0; i < mp->natoms; i++) {
7426 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7427 ap->exflags &= ~kAtomHiddenFlag;
7429 sMoleculeNotifyChangeAppearance(mp);
7434 MoleculeShowReverse(Molecule *mp)
7439 for (i = 0; i < mp->natoms; i++) {
7440 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7441 ap->exflags ^= kAtomHiddenFlag;
7443 sMoleculeUnselectHiddenAtoms(mp);
7444 sMoleculeNotifyChangeAppearance(mp);
7449 MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
7452 if (mp == NULL || ig == NULL)
7454 for (i = 0; i < mp->natoms; i++) {
7455 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7456 if (ap->exflags & kAtomHiddenFlag)
7457 continue; /* Already hidden */
7458 if (IntGroupLookupPoint(ig, i) >= 0)
7459 ap->exflags |= kAtomHiddenFlag;
7461 sMoleculeUnselectHiddenAtoms(mp);
7462 sMoleculeNotifyChangeAppearance(mp);
7466 #pragma mark ====== Reversible Editing ======
7470 sMoleculeNotifyModification(Molecule *mp)
7472 ** TODO: Finer control of notification types may be necessary **
7473 MoleculeCallback_notifyModification(mp, 0);
7477 /* Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup */
7479 sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
7482 if (where == NULL) {
7483 /* Append the new objects at the end */
7484 memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
7487 n1 = IntGroupGetCount(where); /* Position to get new object */
7488 n2 = nobjs; /* Position to get old object */
7489 n3 = n1 + n2; /* Position to place new/old object */
7490 for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
7491 int start = IntGroupGetStartPoint(where, i);
7492 int end = IntGroupGetEndPoint(where, i);
7494 /* old[end-(n3-n2)..n2-1] is moved to old[end..n3-1] */
7495 memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
7496 n2 = end - (n3 - n2);
7499 /* new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1] */
7500 memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
7507 /* Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup */
7509 sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
7511 int n1, n2, n3, start, end, i;
7512 if (where == NULL || IntGroupGetCount(where) == 0)
7513 return 0; /* No operation */
7514 if (objs == NULL || nobjs == 0)
7515 return 1; /* Bad argument */
7516 n1 = 0; /* Position to move remaining elements to */
7517 n2 = 0; /* Position to move remaining elements from */
7518 n3 = 0; /* Position to move removed elements to */
7519 for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
7520 end = IntGroupGetEndPoint(where, i);
7522 /* Move (start - n2) elements from objs[n2] to objs[n1] */
7524 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
7528 /* Move (end - start) elements from objs[n2] to clip[n3] */
7530 memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
7531 n3 += (end - start);
7532 n2 += (end - start);
7534 /* Move (nobjs - n2) elements from objs[n2] to objs[n1] */
7536 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
7540 /* Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup */
7542 sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
7544 int n1, start, end, i;
7545 if (objs == NULL || where == NULL)
7546 return 1; /* Bad argument */
7547 n1 = 0; /* Position to move removed elements to */
7548 for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
7549 end = IntGroupGetEndPoint(where, i);
7550 /* Copy (end - start) elements from objs[start] to clip[n1] */
7552 memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
7553 n1 += (end - start);
7558 /* Create a new atom with no bonding information. ap must _not_ be inside the given molecule
7559 (Use AtomDuplicate() first) */
7561 MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
7565 if (mp == NULL || ap == NULL || mp->noModifyTopology)
7568 if (pos < 0 || pos >= mp->natoms)
7570 ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
7572 goto error; /* Out of memory */
7573 ap1 = ATOM_AT_INDEX(mp->atoms, pos);
7574 if (pos < mp->natoms - 1) {
7575 memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
7577 if (AtomDuplicate(ap1, ap) == NULL) {
7578 /* Cannot duplicate: restore the original state */
7579 memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
7583 ap1->connect.count = 0;
7584 if (ap1->resSeq >= mp->nresidues)
7585 AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
7586 if (ap1->resName[0] == 0)
7587 strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
7588 if (ap1->segName[0] == 0)
7589 strncpy(ap1->segName, "MAIN", 4);
7590 if (pos < mp->natoms - 1) {
7591 /* Renumber the connect table, bonds, angles, etc. */
7592 for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
7595 cp = AtomConnectData(&api->connect);
7596 for (j = 0; j < api->connect.count; j++) {
7600 if (api->anchor != NULL) {
7601 cp = AtomConnectData(&api->anchor->connect);
7602 for (j = 0; j < api->anchor->connect.count; j++) {
7608 for (i = 0; i < mp->nbonds * 2; i++) {
7609 if (mp->bonds[i] >= pos)
7612 for (i = 0; i < mp->nangles * 3; i++) {
7613 if (mp->angles[i] >= pos)
7616 for (i = 0; i < mp->ndihedrals * 4; i++) {
7617 if (mp->dihedrals[i] >= pos)
7620 for (i = 0; i < mp->nimpropers * 4; i++) {
7621 if (mp->impropers[i] >= pos)
7625 mp->nframes = -1; /* Should be recalculated later */
7626 MoleculeIncrementModifyCount(mp);
7627 mp->needsMDRebuild = 1;
7628 __MoleculeUnlock(mp);
7631 __MoleculeUnlock(mp);
7637 static int s_error_count;
7640 s_fprintf(FILE *fp, const char *fmt, ...)
7645 return vfprintf(fp, fmt, va);
7649 MoleculeCheckSanity(Molecule *mol)
7651 const char *fail = "Sanity check failure";
7652 Int i, j, *ip, c[4];
7655 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7656 if (ap->resSeq >= mol->nresidues)
7657 s_fprintf(stderr, "%s: atom %d residue %d but nresidues %d\n", fail, i, ap->resSeq, mol->nresidues);
7658 if (ap->type != 0 && ap->type < kAtomTypeMinimum)
7659 s_fprintf(stderr, "%s: atom %d atom type %d less than minimum\n", fail, i, ap->type);
7660 if (ap->atomicNumber < 0 || ap->atomicNumber > 113)
7661 s_fprintf(stderr, "%s: atom %d atomic number %d\n", fail, i, ap->atomicNumber);
7662 ip = AtomConnectData(&ap->connect);
7663 for (j = 0; j < ap->connect.count; j++) {
7664 if (ip[j] < 0 || ip[j] >= mol->natoms)
7665 s_fprintf(stderr, "%s: atom %d connect[%d] = %d out of range\n", fail, i, j, ip[j]);
7666 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[j])->connect), i) == 0)
7667 s_fprintf(stderr, "%s: atom %d has connect %d but atom %d has no connect %d\n", fail, i, ip[j], ip[j], i);
7670 for (i = 0, ip = mol->bonds; i < mol->nbonds; i++, ip += 2) {
7671 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms)
7672 s_fprintf(stderr, "%s: bond %d %d-%d out of range\n", fail, i, ip[0], ip[1]);
7673 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[0])->connect), ip[1]) == 0)
7674 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]);
7676 for (i = 0, ip = mol->angles; i < mol->nangles; i++, ip += 3) {
7677 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms || ip[2] < 0 || ip[2] >= mol->natoms)
7678 s_fprintf(stderr, "%s: angle %d %d-%d-%d out of range\n", fail, i, ip[0], ip[1], ip[2]);
7679 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
7681 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]);
7682 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
7684 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]);
7685 if (c[0] == 2 && c[1] == 2)
7686 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]);
7688 for (i = 0, ip = mol->dihedrals; i < mol->ndihedrals; i++, ip += 4) {
7689 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)
7690 s_fprintf(stderr, "%s: dihedral %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
7691 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
7692 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
7693 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
7695 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]);
7697 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]);
7699 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]);
7701 for (i = 0, ip = mol->impropers; i < mol->nimpropers; i++, ip += 4) {
7702 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)
7703 s_fprintf(stderr, "%s: improper %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
7704 c[0] = MoleculeAreAtomsConnected(mol, ip[2], ip[0]);
7705 c[1] = MoleculeAreAtomsConnected(mol, ip[2], ip[1]);
7706 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
7708 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]);
7710 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]);
7712 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]);
7714 return s_error_count;
7718 /* Merge two molecules. We use this procedure for all add-atom operations. */
7719 /* resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
7720 /* If nactions and actions are non-NULL, then the corresponding undo actions are created and returned. */
7721 /* If forUndo is non-zero, then only the atoms are inserted; other information should be inserted
7722 separately by other undo actions. */
7724 MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, Int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
7727 Int i, j, n1, n2, n3, n4, *cp;
7728 Int *new2old, *old2new;
7733 if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
7734 return 0; /* Do nothing */
7736 if (dst->noModifyTopology)
7737 return 1; /* Prohibited operation */
7739 if (where != NULL && IntGroupGetCount(where) != src->natoms)
7740 return 1; /* Bad parameter */
7742 if (nactions != NULL)
7744 if (actions != NULL)
7748 __MoleculeLock(dst);
7752 if (resSeqOffset < 0)
7755 /* Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
7756 and ndst..ndst+nsrc-1 are for atoms in src. */
7757 new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
7758 if (new2old == NULL)
7760 old2new = new2old + ndst + nsrc;
7761 n1 = 0; /* dst index */
7762 n2 = 0; /* src index */
7763 n3 = 0; /* "merged" index */
7765 while (n1 < ndst || n2 < nsrc) {
7766 if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
7769 /* n4 elements from dst[n1] will go to merged[n3] */
7770 for (j = 0; j < n4; j++) {
7771 old2new[n1 + j] = n3 + j;
7772 new2old[n3 + j] = n1 + j;
7776 if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
7778 /* n4 elements from src[n2] will go to merged[n3] */
7779 for (j = 0; j < n4; j++) {
7780 old2new[ndst + n2 + j] = n3 + j;
7781 new2old[n3 + j] = ndst + n2 + j;
7788 /* Expand the destination array */
7789 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
7792 /* Move the atoms */
7793 if (where == NULL) {
7794 /* Duplicate atoms to the end of the destination array */
7795 for (i = 0; i < nsrc; i++) {
7796 ap = ATOM_AT_INDEX(dst->atoms, ndst + i);
7797 if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7799 if (forUndo) /* For undo action, all bonds come from another undo action, so connection info are cleared */
7800 AtomConnectResize(&ap->connect, 0);
7803 /* Duplicate to a temporary storage and then insert */
7804 Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
7805 if (tempatoms == NULL)
7807 for (i = 0; i < nsrc; i++) {
7808 ap = ATOM_AT_INDEX(tempatoms, i);
7809 if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7811 if (forUndo) /* See above */
7812 AtomConnectResize(&ap->connect, 0);
7814 if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
7818 dst->natoms = ndst + nsrc;
7820 /* Renumber the atom indices in connect[] and symbase, and modify the residue numbers */
7821 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7822 if (new2old[i] < ndst) {
7823 /* This atom is from dst */
7826 /* This atom is from src */
7827 n1 = ndst; /* Offset to the internal number */
7828 if (ap->resSeq != 0)
7829 ap->resSeq += resSeqOffset; /* Modify residue number */
7831 cp = AtomConnectData(&ap->connect);
7832 for (j = 0; j < ap->connect.count; j++)
7833 cp[j] = old2new[cp[j] + n1];
7834 if (SYMOP_ALIVE(ap->symop))
7835 ap->symbase = old2new[ap->symbase + n1];
7836 if (ap->anchor != NULL) {
7837 cp = AtomConnectData(&ap->anchor->connect);
7838 for (j = 0; j < ap->anchor->connect.count; j++)
7839 cp[j] = old2new[cp[j] + n1];
7843 /* Move the bonds, angles, dihedrals, impropers */
7844 for (i = 0; i < 4; i++) {
7845 Int *nitems, *nitems_src;
7846 Int **items, **items_src;
7847 Int nsize; /* Number of Ints in one element */
7850 nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
7852 nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
7854 nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
7856 nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
7858 nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
7859 items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
7861 /* During undo, no bonds etc. are copied from src; they will be taken care later
7866 /* Keep the old number of entries in dst, because it is updated by AssignArray() */
7868 /* Also keep the old number of entries in src, in case src and dst point the same molecule */
7870 /* Expand the array */
7871 if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
7873 /* Copy the items */
7874 memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
7876 /* Copy the bond order info if present */
7877 Int nn1 = dst->nbondOrders;
7878 if (dst->bondOrders != NULL || src->bondOrders != NULL) {
7879 if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), dst->nbonds - 1, NULL) == NULL)
7881 memset(dst->bondOrders + nn1, 0, sizeof(Double) * (dst->nbonds - nn1));
7882 if (src->bondOrders != NULL)
7883 memmove(dst->bondOrders + n1, src->bondOrders, sizeof(Double) * n2);
7888 for (j = 0; j < n1 * nsize; j++)
7889 (*items)[j] = old2new[(*items)[j]];
7890 for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
7891 (*items)[j] = old2new[(*items)[j] + ndst];
7892 if (forUndo == 0 && actions != NULL) {
7893 ig = IntGroupNewWithPoints(n1, n2, -1);
7895 case 0: act = MolActionNew(gMolActionDeleteBonds, ig); break;
7896 case 1: act = MolActionNew(gMolActionDeleteAngles, ig); break;
7897 case 2: act = MolActionNew(gMolActionDeleteDihedrals, ig); break;
7898 case 3: act = MolActionNew(gMolActionDeleteImpropers, ig); break;
7900 IntGroupRelease(ig);
7901 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7906 /* Renumber existing parameters */
7907 if (dst->par != NULL) {
7909 for (type = kFirstParType; type <= kLastParType; type++) {
7911 n1 = ParameterGetCountForType(dst->par, type);
7912 for (i = 0; i < n1; i++) {
7913 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7914 ParameterRenumberAtoms(type, up1, ndst, old2new);
7919 /* Merge parameters from src */
7920 if (src->par != NULL && forUndo == 0) {
7921 UnionPar *up1, *up2;
7923 if (dst->par == NULL)
7924 dst->par = ParameterNew();
7926 /* Renumber existing parameters */
7927 for (type = kFirstParType; type <= kLastParType; type++) {
7928 n1 = ParameterGetCountForType(dst->par, type);
7929 for (i = 0; i < n1; i++) {
7930 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7931 ParameterRenumberAtoms(type, up1, ndst, old2new);
7936 for (type = kFirstParType; type <= kLastParType; type++) {
7937 n1 = ParameterGetCountForType(src->par, type);
7938 n2 = ParameterGetCountForType(dst->par, type);
7941 /* Determine which parameter should be copied from src to dst */
7942 for (i = 0; i < n1; i++) {
7944 up1 = ParameterGetUnionParFromTypeAndIndex(src->par, type, i);
7945 n3 = ParameterGetAtomTypes(type, up1, types);
7946 for (j = 0; j < n3; j++) {
7947 /* If it includes explicit atom index, then it should be copied */
7948 if (types[j] < kAtomTypeMinimum) {
7949 IntGroupAdd(ig, i, 1);
7954 for (j = 0; j < n2; j++) {
7955 up2 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, j);
7956 if (ParameterCompare(up1, up2, type))
7960 /* This is an unknown parameter; should be copied */
7961 IntGroupAdd(ig, i, 1);
7964 n1 = IntGroupGetCount(ig);
7967 up1 = (UnionPar *)calloc(sizeof(UnionPar), n1);
7970 /* Copy parameters and renumber indices if necessary */
7971 for (i = j = 0; i < n1; i++) {
7972 up2 = ParameterGetUnionParFromTypeAndIndex(src->par, type, IntGroupGetNthPoint(ig, i));
7976 ParameterRenumberAtoms(type, up1 + j, nsrc, old2new + ndst);
7979 /* Merge parameters */
7981 IntGroupAdd(ig, n2, j);
7982 if (ParameterInsert(dst->par, type, up1, ig) < j)
7984 if (actions != NULL) {
7985 act = MolActionNew(gMolActionDeleteParameters, type, ig);
7986 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7992 IntGroupRelease(ig);
7995 /* Copy the residues if necessary */
7996 /* src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
7997 However, 1+resSeqOffset should not overwrite the existing residue in dst;
7998 i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1]. */
8000 n1 = dst->nresidues;
8001 if (1 + resSeqOffset < n1) {
8003 } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
8004 if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
8005 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
8007 memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
8008 if (nactions != NULL) {
8009 act = MolActionNew(gMolActionChangeNumberOfResidues, n1);
8010 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
8016 MoleculeCleanUpResidueTable(dst);
8019 dst->nframes = -1; /* Should be recalculated later */
8021 MoleculeIncrementModifyCount(dst);
8022 dst->needsMDRebuild = 1;
8023 __MoleculeUnlock(dst);
8027 __MoleculeUnlock(dst);
8028 Panic("Low memory while adding atoms");
8029 return 1; /* Not reached */
8032 /* Unmerge the molecule. If necessary, the undo actions are stored in nactions/actions array.
8033 (The nactions/actions array must be initialized by the caller) */
8035 sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag, Int *nactions, MolAction ***actions, Int forUndo)
8037 Int nsrc, ndst, nsrcnew;
8038 Int i, j, n1, n2, n3, n4, *cp;
8039 Int *new2old, *old2new;
8040 IntGroup *move_g, *del_g, *remain_g, *dst_par_g, *remove_par_g;
8046 if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
8053 if (src->noModifyTopology && moveFlag)
8054 return 1; /* Prohibit editing */
8056 if ((ndst = IntGroupGetCount(where)) > src->natoms)
8057 return 1; /* Bad parameter */
8059 __MoleculeLock(src);
8064 nsrcnew = nsrc - ndst;
8065 if (resSeqOffset < 0)
8068 /* Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
8069 and nsrcnew..nsrc-1 are for atoms moved into dst. */
8070 new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
8071 if (new2old == NULL)
8073 old2new = new2old + nsrc;
8074 n1 = 0; /* src index */
8075 n2 = 0; /* dst index */
8076 n3 = 0; /* src index after "unmerge" */
8078 while (n1 < nsrc || n2 < ndst) {
8079 if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
8082 /* n4 elements from src[n1] will go to unmerged[n3] */
8083 for (j = 0; j < n4; j++) {
8084 old2new[n1 + j] = n3 + j;
8085 new2old[n3 + j] = n1 + j;
8089 if ((n4 = IntGroupGetInterval(where, i)) < 0)
8091 /* n4 elements from src[n1] will go to dst[n2] */
8092 for (j = 0; j < n4; j++) {
8093 old2new[n1 + j] = nsrcnew + n2 + j;
8094 new2old[nsrcnew + n2 + j] = n1 + j;
8101 /* Atoms to remain in the source group */
8103 remain_g = IntGroupNewWithPoints(0, nsrc, -1);
8104 IntGroupRemoveIntGroup(remain_g, where);
8105 } else remain_g = NULL;
8107 /* Find parameters to be moved to the dst (dst_par_g), and to be removed from the src (remove_par_g) */
8108 if (src->par != NULL) {
8109 dst_par_g = IntGroupNew();
8111 remove_par_g = IntGroupNew();
8112 else remove_par_g = NULL;
8113 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
8114 n2 = ParameterGetCountForType(src->par, n1);
8117 for (i = 0; i < n2; i++) {
8118 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
8119 if (ParameterIsRelevantToAtomGroup(n1, up, src->atoms, where)) {
8120 /* This parameter is to be copied to dst */
8121 IntGroupAdd(dst_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
8123 if (moveFlag && !ParameterIsRelevantToAtomGroup(n1, up, src->atoms, remain_g)) {
8124 /* This parameter is to be removed */
8125 IntGroupAdd(remove_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
8129 } else dst_par_g = remove_par_g = NULL;
8131 /* Pi anchors should be modified if the anchor and its component atoms become separated between
8134 Int ibufsize, *ibuf, flag_i, flag_j;
8136 ibuf = (Int *)malloc(sizeof(Int) * ibufsize);
8137 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
8138 if (ap->anchor == NULL)
8140 flag_i = (old2new[i] < nsrcnew);
8141 cp = AtomConnectData(&ap->anchor->connect);
8142 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
8143 flag_j = (old2new[cp[j]] < nsrcnew);
8144 if (flag_i == flag_j) {
8145 if (n1 >= ibufsize) {
8147 ibuf = (Int *)realloc(ibuf, sizeof(Int) * ibufsize);
8153 /* Need to modify the pi anchor list */
8156 MolActionCreateAndPerform(src, SCRIPT_ACTION("isI"), "set_atom_attr", i, "anchor_list", n1, ibuf);
8161 /* Make a new molecule */
8163 dst = MoleculeNew();
8166 /* Expand the destination array */
8167 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
8169 dst_ap = dst->atoms;
8172 dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
8177 /* Move the atoms */
8179 if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
8181 src->natoms = nsrcnew;
8183 /* The atom record must be deallocated correctly */
8184 for (i = 0; i < ndst; i++)
8185 AtomClean(ATOM_AT_INDEX(dst_ap, i));
8189 for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
8190 AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
8195 /* The dummy destination array is no longer needed */
8200 /* Renumber the atom indices in connect[] (src) */
8202 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
8203 cp = AtomConnectData(&ap->connect);
8204 for (j = n1 = 0; j < ap->connect.count; j++) {
8205 n2 = old2new[cp[j]];
8209 AtomConnectResize(&ap->connect, n1);
8210 if (ap->anchor != NULL) {
8211 cp = AtomConnectData(&ap->anchor->connect);
8212 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
8213 n2 = old2new[cp[j]];
8217 if (n1 != ap->anchor->connect.count) {
8218 /* This should not happen!! */
8219 AtomConnectResize(&ap->anchor->connect, n1);
8220 fprintf(stderr, "Internal error in sMoleculeUnmergeSub (line %d)\n", __LINE__);
8222 free(ap->anchor->coeffs);
8231 /* Renumber the atom indices in connect[] (dst) */
8233 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
8234 if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
8235 ap->resSeq -= resSeqOffset;
8236 else ap->resSeq = 0;
8237 cp = AtomConnectData(&ap->connect);
8238 for (j = n1 = 0; j < ap->connect.count; j++) {
8239 n2 = old2new[cp[j]] - nsrcnew;
8243 AtomConnectResize(&ap->connect, n1);
8244 if (ap->anchor != NULL) {
8245 cp = AtomConnectData(&ap->anchor->connect);
8246 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
8247 n2 = old2new[cp[j]] - nsrcnew;
8251 if (n1 != ap->anchor->connect.count) {
8252 /* This can happen, and the anchor info is silently modified */
8254 AtomConnectResize(&ap->anchor->connect, 0);
8255 free(ap->anchor->coeffs);
8260 AtomConnectResize(&ap->anchor->connect, n1);
8262 for (j = 0; j < n1; j++)
8263 d += ap->anchor->coeffs[j];
8264 for (j = 0; j < n1; j++)
8265 ap->anchor->coeffs[j] /= d;
8266 MoleculeCalculatePiAnchorPosition(dst, i);
8273 /* Separate the bonds, angles, dihedrals, impropers */
8274 /* TODO: Improper torsions should also be copied! */
8275 move_g = IntGroupNew();
8278 for (i = 3; i >= 0; i--) {
8279 Int *nitems, *nitems_dst;
8280 Int **items, **items_dst;
8281 Int nsize; /* Number of Ints in one element */
8282 unsigned char *counts;
8283 del_g = IntGroupNew();
8286 nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
8288 nitems = &src->nangles; items = &src->angles; nsize = 3; break;
8290 nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
8292 nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
8294 nitems = NULL; items = NULL; nsize = 0; break; /* Not reached */
8297 nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
8298 items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
8303 counts = (unsigned char *)calloc(1, *nitems);
8304 /* Find the entries that should be moved to dst */
8306 for (j = 0; j < *nitems * nsize; j++) {
8307 n1 = old2new[(*items)[j]];
8309 counts[j / nsize]++; /* Count the atom belonging to dst */
8311 for (j = n2 = n3 = 0; j < *nitems; j++) {
8312 if (counts[j] > 0) {
8313 /* Remove from src */
8315 if (IntGroupAdd(del_g, j, 1) != 0)
8317 if (counts[j] == nsize) {
8320 if (IntGroupAdd(move_g, j, 1) != 0)
8326 /* Expand the destination array */
8327 if (items_dst != NULL && n3 > 0) {
8328 if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
8330 if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
8332 if (i == 0 && src->bondOrders != NULL) {
8333 if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), n3 - 1, NULL) == NULL)
8335 if (sCopyElementsFromArrayAtPositions(src->bondOrders, src->nbondOrders, dst->bondOrders, sizeof(Double), move_g) != 0)
8339 /* Remove from src */
8340 if (moveFlag && forUndo == 0) {
8341 if (nactions != NULL) {
8344 ip = (Int *)malloc(sizeof(Int) * nsize * n2);
8345 for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
8346 memmove(ip + j * nsize, *items + k * nsize, sizeof(Int) * nsize);
8347 if (i == 0 && src->bondOrders != NULL) {
8348 dp = (Double *)malloc(sizeof(Double) * n2);
8349 for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
8350 dp[j] = src->bondOrders[k];
8354 act = MolActionNew(gMolActionAddBondsForUndo, n2 * nsize, ip, del_g); break;
8356 act = MolActionNew(gMolActionAddAngles, n2 * nsize, ip, del_g); break;
8358 act = MolActionNew(gMolActionAddDihedrals, n2 * nsize, ip, del_g); break;
8360 act = MolActionNew(gMolActionAddImpropers, n2 * nsize, ip, del_g); break;
8363 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
8368 act = MolActionNew(gMolActionAssignBondOrders, n2, dp, del_g);
8369 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
8374 if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
8379 /* Renumber the entries */
8381 for (j = 0; j < *nitems * nsize; j++) {
8382 (*items)[j] = old2new[(*items)[j]];
8385 if (items_dst != NULL) {
8386 for (j = 0; j < *nitems_dst * nsize; j++) {
8387 (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
8391 IntGroupClear(move_g);
8392 IntGroupRelease(del_g);
8394 IntGroupRelease(move_g);
8396 /* Copy the residues */
8398 /* src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset) */
8399 n1 = src->nresidues - resSeqOffset; /* This will be dst->nresidues (if >0) */
8400 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
8403 memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
8407 /* Copy the parameters to dst */
8408 if (dst != NULL && dst_par_g != NULL && (n2 = IntGroupGetCount(dst_par_g)) > 0) {
8409 IntGroup *dst_new_g = IntGroupNew();
8410 Int dst_par_count[kLastParType - kFirstParType + 1];
8411 if (dst_new_g == NULL)
8413 for (i = 0; i <= kLastParType - kFirstParType; i++)
8414 dst_par_count[i] = 0;
8415 up = (UnionPar *)calloc(sizeof(UnionPar), n2);
8418 if (ParameterCopy(src->par, kFirstParType, up, dst_par_g) < n2)
8420 /* Renumber the explicit atom indices */
8421 for (i = 0; i < nsrc; i++)
8422 old2new[i] -= nsrcnew; /* new indices for atoms in dst; otherwise negative numbers */
8423 for (i = 0; i < n2; i++) {
8424 /* Renumber the indices, and count the number of parameters for each type */
8425 n1 = kFirstParType + IntGroupGetNthPoint(dst_par_g, i) / kParameterIndexOffset;
8426 dst_par_count[n1 - kFirstParType]++;
8427 ParameterRenumberAtoms(n1, up + i, nsrc, old2new);
8429 for (i = 0; i < nsrc; i++)
8430 old2new[i] += nsrcnew;
8431 if (dst->par == NULL)
8432 dst->par = ParameterNew();
8433 for (i = 0; i <= kLastParType - kFirstParType; i++) {
8434 if (dst_par_count[i] > 0)
8435 IntGroupAdd(dst_new_g, i * kParameterIndexOffset, dst_par_count[i]);
8437 if (ParameterInsert(dst->par, kFirstParType, up, dst_new_g) < n2)
8440 IntGroupRelease(dst_new_g);
8442 IntGroupRelease(dst_par_g);
8444 /* Remove the unused parameter. Note: the parameters that are in remove_par_g and not in
8445 dst_par_g will disappear. To support undo, these parameters should be taken care separately. */
8446 if (forUndo == 0 && remove_par_g != NULL && (n2 = IntGroupGetCount(remove_par_g)) > 0) {
8447 UnionPar *up = (UnionPar *)malloc(sizeof(UnionPar) * n2);
8448 ParameterDelete(src->par, kFirstParType, up, remove_par_g);
8449 if (nactions != NULL) {
8450 act = MolActionNew(gMolActionAddParameters, kFirstParType, remove_par_g, n2, up);
8451 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
8456 IntGroupRelease(remove_par_g);
8458 /* Renumber the parameter records remaining in the src */
8460 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
8461 n2 = ParameterGetCountForType(src->par, n1);
8462 for (i = 0; i < n2; i++) {
8463 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
8464 ParameterRenumberAtoms(n1, up, nsrc, old2new);
8470 IntGroupRelease(remain_g);
8471 MoleculeCleanUpResidueTable(src);
8473 MoleculeCleanUpResidueTable(dst);
8476 src->nframes = -1; /* Should be recalculated later */
8478 dst->nframes = -1; /* Should be recalculated later */
8484 MoleculeIncrementModifyCount(src);
8485 src->needsMDRebuild = 1;
8486 __MoleculeUnlock(src);
8491 __MoleculeUnlock(src);
8492 /* Panic("Low memory while removing atoms"); */
8496 /* Separate molecule into two parts. The atoms specified by 'where' are moved
8497 from src to a new molecule, which is returned as *dstp. Dstp can be NULL,
8498 in which case the moved atoms are discarded. */
8500 MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
8502 return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1, nactions, actions, forUndo);
8505 /* Extract atoms from a given molecule into two parts. The atoms specified by
8506 'where' are copied from src to a new molecule, which is returned as *dstp.
8507 If dummyFlag is non-zero, then the atoms that are not included in the group
8508 but are connected to any atoms in the group are converted to "dummy" atoms
8509 (i.e. with element "Du" and names beginning with an underscore) and included
8510 in the new molecule object. */
8512 MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
8516 /* Extract the fragment */
8517 retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0, NULL, NULL, 0);
8523 /* Search bonds crossing the molecule border */
8524 IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
8526 IntGroupIterator iter;
8529 IntGroupIteratorInit(ig, &iter);
8530 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8531 /* The atoms at the border */
8534 n1 = src->bonds[i*2];
8535 n2 = src->bonds[i*2+1];
8536 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
8540 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
8541 continue; /* Actually this is an internal error */
8543 /* n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule */
8544 /* Create a new dummy atom with the same segment/residue info with n1
8545 and the same position as n2 */
8546 ap = ATOM_AT_INDEX(src->atoms, n1);
8547 memset(&a, 0, gSizeOfAtomRecord);
8548 a.segSeq = ap->segSeq;
8549 memmove(a.segName, ap->segName, 4);
8550 a.resSeq = ap->resSeq;
8551 memmove(a.resName, ap->resName, 4);
8552 ElementToString(0, a.element); /* "Du" */
8553 snprintf(a.aname, 4, "_%d", idx++);
8554 a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
8555 /* Add the dummy atom to the new molecule; nn[1] is the index
8556 of the new dummy atom in the new molecule */
8557 nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
8558 /* Connect nn1 and nn2 */
8559 nn[2] = kInvalidIndex;
8560 MoleculeAddBonds(*dstp, 1, nn, NULL, 1);
8562 IntGroupIteratorRelease(&iter);
8563 IntGroupRelease(ig);
8571 MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds, IntGroup *where, Int autoGenerate)
8573 Int nangles, ndihedrals;
8574 Int *angles, *dihedrals;
8575 Int i, j, k, kk, n1, n2, cn1, cn2;
8578 Atom *ap1, *ap2, *ap3;
8580 if (mp == NULL || bonds == NULL || nbonds <= 0)
8582 if (mp->noModifyTopology)
8583 return -4; /* Prohibited operation */
8585 /* Note: Duplicates and validity are not checked (the caller must do that) */
8590 if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, n1 + nbonds - 1, NULL) == NULL
8591 || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nbonds, sizeof(Int) * 2, where) != 0) {
8592 __MoleculeUnlock(mp);
8593 return -4; /* Out of memory */
8595 if (mp->bondOrders != NULL) {
8596 /* Expand the bond order info (all new entries are zero) */
8597 Double *dp = (Double *)calloc(sizeof(Double), nbonds);
8600 if (AssignArray(&(mp->bondOrders), &(mp->nbondOrders), sizeof(Double), n1 + nbonds - 1, NULL) == NULL
8601 || sInsertElementsToArrayAtPositions(mp->bondOrders, n1, dp, nbonds, sizeof(Double), where) != 0) {
8602 __MoleculeUnlock(mp);
8609 angles = dihedrals = NULL;
8610 nangles = ndihedrals = 0;
8612 /* Add connects[], and angles/dihedrals (if autoGenerate is true) */
8613 for (i = 0; i < nbonds; i++) {
8615 /* One entry at time */
8616 /* (Otherwise, duplicate entries of angles and dihedrals result) */
8618 n2 = bonds[i * 2 + 1];
8620 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
8621 AtomConnectInsertEntry(&ap1->connect, -1, n2);
8622 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
8623 AtomConnectInsertEntry(&ap2->connect, -1, n1);
8625 /* Add angles and dihedrals */
8627 AtomConnect *ac1, *ac2;
8628 if (ap1->anchor == NULL || ap2->anchor == NULL) {
8629 /* N1-N2-{XY} or N2-N1-{XY} angles (X: connected atom, Y: constitute atom of pi-anchor) */
8630 for (j = 0; j < 4; j++) {
8632 case 0: temp[0] = n1; temp[1] = n2; ac1 = &ap2->connect; break; /* N1-N2-X */
8633 case 1: if (ap2->anchor == NULL) continue; else ac1 = &ap2->anchor->connect; break; /* N1-N2-Y */
8634 case 2: temp[0] = n2; temp[1] = n1; ac1 = &ap1->connect; break; /* N2-N1-X */
8635 case 3: if (ap1->anchor == NULL) continue; else ac1 = &ap1->anchor->connect; break; /* N2-N1-Y */
8637 cp1 = AtomConnectData(ac1);
8639 for (k = 0; k < cn1; k++) {
8641 if (temp[2] == temp[0])
8643 ap3 = ATOM_AT_INDEX(mp->atoms, temp[2]);
8644 if (ap3->anchor != NULL) {
8645 /* Avoid X-anchor-anchor angle (anchor-X-anchor is allowed) */
8646 if ((j < 2 && ap2->anchor != NULL) || (j >= 2 && ap1->anchor != NULL))
8649 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
8651 /* Dihedrals N1-N2-X-{XY} or N2-N1-X-{XY} */
8652 if (j == 1 || j == 3)
8654 cp2 = AtomConnectData(&ap3->connect);
8655 for (kk = 0; kk < ap3->connect.count; kk++) {
8657 if (temp[3] == temp[0] || temp[3] == temp[1])
8659 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8662 if (ap3->anchor != NULL) {
8663 /* N1-N2-X-Y or N2-N1-X-Y */
8664 /* for Y, only the first constitute atom is considered */
8665 cp2 = AtomConnectData(&ap3->anchor->connect);
8667 if (temp[3] == temp[0] || temp[3] == temp[1])
8669 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8675 /* X-N1-N2-X dihedrals */
8676 /* Y-N1-N2-anchor is allowed, but the force may be zero if the angle N1-N2-anchor is */
8677 /* close to 180 deg (e.g. in ferrocene, C-anchor-Fe-anchor dihedral should be k=0) */
8678 if (ap1->anchor == NULL) {
8679 ac1 = &ap1->connect;
8682 ac1 = &ap1->anchor->connect;
8683 cn1 = 1; /* Only the first constitute atom of pi-anchor is considered */
8685 if (ap2->anchor == NULL) {
8686 ac2 = &ap2->connect;
8689 ac2 = &ap2->anchor->connect;
8690 cn2 = 1; /* Only the first constitute atom of pi-anchor is considered */
8694 cp1 = AtomConnectData(ac1);
8695 cp2 = AtomConnectData(ac2);
8696 for (j = 0; j < cn1; j++) {
8698 if (temp[0] == temp[2])
8700 for (k = 0; k < cn2; k++) {
8702 if (temp[3] == temp[0] || temp[3] == temp[1])
8704 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8711 if (angles != NULL) {
8712 temp[0] = kInvalidIndex;
8713 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
8715 MoleculeAddAngles(mp, angles, NULL);
8718 if (dihedrals != NULL) {
8719 temp[0] = kInvalidIndex;
8720 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8722 MoleculeAddDihedrals(mp, dihedrals, NULL);
8726 MoleculeIncrementModifyCount(mp);
8727 mp->needsMDRebuild = 1;
8728 __MoleculeUnlock(mp);
8733 __MoleculeUnlock(mp);
8734 Panic("Low memory while adding bonds");
8735 return -1; /* Not reached */
8739 /* The deleted angles and dihedrals are stored in outRemoval. */
8740 /* (*outRemoval) is an array of integers, containing:
8741 [0..na*3-1]: the angle indices
8742 [na*3..na*3+nd*4-1]: the dihedral indices
8743 [na*3+nd*4..na*3+nd*4+ni*4-1]: the improper indices
8744 *outRemovedPos is an intgroup denoting the positions of the removed angles/dihedrals/impropers.
8745 the angle indices are included as they are,
8746 the dihedral indices are offset by ATOMS_MAX_NUMBER,
8747 the improper indices are offset by ATOMS_MAX_NUMBER*2.
8748 Note: the removed bond indices are not returned, because the caller should already know them. */
8750 MoleculeDeleteBonds(Molecule *mp, Int *bonds, IntGroup *where, Int **outRemoved, IntGroup **outRemovedPos)
8752 Int i, j, n1, n2, nw;
8753 Int *ip, *jp, na, nd, ni;
8754 IntGroup *ag, *dg, *ig;
8756 IntGroupIterator iter;
8760 if (mp->noModifyTopology)
8761 return -4; /* Prohibited operation */
8765 /* Update connects[] */
8766 IntGroupIteratorInit(where, &iter);
8767 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8768 n1 = mp->bonds[i * 2];
8769 n2 = mp->bonds[i * 2 + 1];
8770 ap = ATOM_AT_INDEX(mp->atoms, n1);
8771 ip = AtomConnectData(&ap->connect);
8772 for (j = 0; j < ap->connect.count; j++) {
8774 AtomConnectDeleteEntry(&ap->connect, j);
8778 ap = ATOM_AT_INDEX(mp->atoms, n2);
8779 ip = AtomConnectData(&ap->connect);
8780 for (j = 0; j < ap->connect.count; j++) {
8782 AtomConnectDeleteEntry(&ap->connect, j);
8788 /* Remove bonds, angles, dihedrals, impropers */
8793 nw = IntGroupGetCount(where);
8794 jp = (Int *)malloc(sizeof(Int) * nw * 2);
8796 IntGroupIteratorReset(&iter);
8797 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8798 jp[j++] = mp->bonds[i * 2];
8799 jp[j++] = mp->bonds[i * 2 + 1];
8801 IntGroupIteratorRelease(&iter);
8803 for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8804 for (j = 0; j < nw; j++) {
8807 if ((ip[0] == n1 && ip[1] == n2)
8808 || (ip[1] == n1 && ip[0] == n2)
8809 || (ip[1] == n1 && ip[2] == n2)
8810 || (ip[2] == n1 && ip[1] == n2)) {
8811 if (IntGroupAdd(ag, i, 1) != 0)
8818 for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8819 for (j = 0; j < nw; j++) {
8822 if ((ip[0] == n1 && ip[1] == n2)
8823 || (ip[1] == n1 && ip[0] == n2)
8824 || (ip[1] == n1 && ip[2] == n2)
8825 || (ip[2] == n1 && ip[1] == n2)
8826 || (ip[2] == n1 && ip[3] == n2)
8827 || (ip[3] == n1 && ip[2] == n2)) {
8830 if (IntGroupAdd(dg, i, 1) != 0)
8837 for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8838 for (j = 0; j < nw; j++) {
8841 if ((ip[0] == n1 && ip[2] == n2)
8842 || (ip[1] == n1 && ip[2] == n2)
8843 || (ip[3] == n1 && ip[2] == n2)
8844 || (ip[0] == n2 && ip[2] == n1)
8845 || (ip[1] == n2 && ip[2] == n1)
8846 || (ip[3] == n2 && ip[2] == n1)) {
8849 if (IntGroupAdd(ig, i, 1) != 0)
8858 if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, bonds, sizeof(Int) * 2, where) != 0)
8860 mp->nbonds -= IntGroupGetCount(where);
8861 if (mp->nbonds == 0) {
8865 if (mp->bondOrders != NULL) {
8866 if (sRemoveElementsFromArrayAtPositions(mp->bondOrders, mp->nbondOrders, NULL, sizeof(Double), where) != 0)
8868 mp->nbondOrders -= IntGroupGetCount(where);
8869 if (mp->nbondOrders == 0) {
8870 free(mp->bondOrders);
8871 mp->bondOrders = NULL;
8874 if (na == 0 && nd == 0 && ni == 0)
8877 ip = (Int *)malloc(sizeof(Int) * (na * 3 + nd * 4 + ni * 4));
8879 MoleculeDeleteAngles(mp, ip, ag);
8881 MoleculeDeleteDihedrals(mp, ip + na * 3, dg);
8883 MoleculeDeleteImpropers(mp, ip + na * 3 + nd * 4, ig);
8885 IntGroupOffset(dg, ATOMS_MAX_NUMBER);
8886 IntGroupOffset(ig, ATOMS_MAX_NUMBER * 2);
8887 IntGroupAddIntGroup(ag, dg);
8888 IntGroupAddIntGroup(ag, ig);
8889 IntGroupRelease(dg);
8890 IntGroupRelease(ig);
8893 if (IntGroupGetCount(ag) == 0) {
8894 IntGroupRelease(ag);
8899 *outRemovedPos = ag;
8901 MoleculeIncrementModifyCount(mp);
8902 mp->needsMDRebuild = 1;
8903 __MoleculeUnlock(mp);
8905 return na * 3 + nd * 4 + ni * 4;
8908 __MoleculeUnlock(mp);
8909 Panic("Low memory while removing bonds");
8910 return -1; /* Not reached */
8914 MoleculeAssignBondOrders(Molecule *mp, const Double *orders, IntGroup *where)
8917 IntGroupIterator iter;
8918 if (mp == NULL || orders == NULL || mp->nbonds == 0)
8920 if (mp->noModifyTopology)
8921 return -4; /* Prohibited operation */
8922 if (mp->bondOrders == NULL) {
8923 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbonds - 1, NULL);
8924 memset(mp->bondOrders, 0, sizeof(Double) * mp->nbondOrders);
8926 IntGroupIteratorInit(where, &iter);
8928 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8929 if (i >= mp->nbondOrders)
8931 mp->bondOrders[i] = orders[j++];
8933 IntGroupIteratorRelease(&iter);
8938 MoleculeGetBondOrders(Molecule *mp, Double *outOrders, IntGroup *where)
8941 IntGroupIterator iter;
8942 if (mp == NULL || mp->nbonds == 0)
8944 if (mp->bondOrders == NULL) {
8945 /* Returns all zero */
8946 i = IntGroupGetCount(where);
8947 for (j = 0; j < i; j++)
8950 IntGroupIteratorInit(where, &iter);
8952 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8953 if (i < mp->nbondOrders)
8954 outOrders[j] = mp->bondOrders[i];
8955 else outOrders[j] = 0.0;
8963 MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
8966 if (mp == NULL || angles == NULL)
8968 if (mp->noModifyTopology)
8969 return -4; /* Prohibited operation */
8973 nc = IntGroupGetCount(where);
8975 for (n1 = 0; angles[n1 * 3] >= 0; n1++)
8981 if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
8982 || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
8983 __MoleculeUnlock(mp);
8984 Panic("Low memory while adding angles");
8987 mp->needsMDRebuild = 1;
8988 __MoleculeUnlock(mp);
8993 MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
8996 if (mp == NULL || where == NULL)
8998 if (mp->noModifyTopology)
8999 return -4; /* Prohibited operation */
9001 if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
9002 __MoleculeUnlock(mp);
9003 Panic("Bad argument while deleting angles");
9005 mp->nangles -= (nc = IntGroupGetCount(where));
9006 if (mp->nangles == 0) {
9010 mp->needsMDRebuild = 1;
9011 __MoleculeUnlock(mp);
9016 MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
9019 if (mp == NULL || dihedrals == NULL)
9021 if (mp->noModifyTopology)
9022 return -4; /* Prohibited operation */
9024 nc = IntGroupGetCount(where);
9026 for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
9032 n1 = mp->ndihedrals;
9034 if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
9035 || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
9036 __MoleculeUnlock(mp);
9037 Panic("Low memory while adding dihedrals");
9039 mp->needsMDRebuild = 1;
9040 __MoleculeUnlock(mp);
9045 MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
9048 if (mp == NULL || where == NULL)
9050 if (mp->noModifyTopology)
9051 return -4; /* Prohibited operation */
9053 if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
9054 __MoleculeUnlock(mp);
9055 Panic("Internal error: bad argument while deleting dihedrals");
9057 mp->ndihedrals -= (nc = IntGroupGetCount(where));
9058 if (mp->ndihedrals == 0) {
9059 free(mp->dihedrals);
9060 mp->dihedrals = NULL;
9062 mp->needsMDRebuild = 1;
9063 __MoleculeUnlock(mp);
9068 MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
9071 if (mp == NULL || impropers == NULL)
9073 if (mp->noModifyTopology)
9074 return -4; /* Prohibited operation */
9076 nc = IntGroupGetCount(where);
9078 for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
9084 n1 = mp->nimpropers;
9086 if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
9087 || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
9088 __MoleculeUnlock(mp);
9089 Panic("Low memory while adding impropers");
9091 mp->needsMDRebuild = 1;
9092 __MoleculeUnlock(mp);
9097 MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
9100 if (mp == NULL || where == NULL)
9102 if (mp->noModifyTopology)
9103 return -4; /* Prohibited operation */
9105 if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
9106 __MoleculeUnlock(mp);
9107 Panic("Internal error: bad argument while deleting impropers");
9109 mp->nimpropers -= (nc = IntGroupGetCount(where));
9110 if (mp->impropers == NULL) {
9111 free(mp->impropers);
9112 mp->impropers = NULL;
9114 __MoleculeUnlock(mp);
9119 MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
9122 if (mp == NULL || mp->bonds == NULL)
9124 for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
9125 if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
9132 MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
9135 if (mp == NULL || mp->angles == NULL)
9137 for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
9138 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
9139 (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
9146 MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
9149 if (mp == NULL || mp->dihedrals == NULL)
9151 for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
9152 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
9153 (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
9160 MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
9163 if (mp == NULL || mp->impropers == NULL)
9165 for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
9168 if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
9169 (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
9170 (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
9176 /* Remove the bond at bondIndex and create two dummy atoms instead.
9177 The dummy atoms are placed at the end of atoms[], and the residue
9178 numbers are the same as the root atoms (i.e. the atoms to which
9179 the dummy atoms are connected). The indices are returned in
9180 dummyIndices[0,1]. */
9182 MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
9184 Int roots[3], newBonds[5];
9190 if (mp == NULL || mp->noModifyTopology)
9192 if (bondIndex < 0 || bondIndex >= mp->nbonds)
9194 roots[0] = mp->bonds[bondIndex * 2];
9195 roots[1] = mp->bonds[bondIndex * 2 + 1];
9196 roots[2] = kInvalidIndex;
9197 rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
9198 rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
9199 VecSub(dr, rootp[0]->r, rootp[1]->r);
9200 for (i = 0; i < 2; i++) {
9203 memmove(nap, rootp[i], sizeof(na));
9204 nap->aname[0] = '*';
9205 strcpy(nap->element, "Du");
9207 nap->charge = nap->weight = 0.0;
9208 nap->atomicNumber = 0;
9209 nap->connect.count = 0;
9210 w = (i == 0 ? 0.4 : -0.4);
9211 VecScaleInc(nap->r, dr, w);
9218 /* Expand atoms array and append the dummy atoms at the end */
9220 natoms = mp->natoms;
9221 if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
9223 memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
9224 dummyIndices[0] = natoms;
9225 dummyIndices[1] = natoms + 1;
9227 /* Remove the old bond and create new bonds */
9228 ig = IntGroupNewWithPoints(bondIndex, 1, -1);
9231 MoleculeDeleteBonds(mp, NULL, ig, NULL, NULL);
9232 IntGroupRelease(ig);
9233 newBonds[0] = roots[0];
9234 newBonds[1] = dummyIndices[0];
9235 newBonds[2] = roots[1];
9236 newBonds[3] = dummyIndices[1];
9237 newBonds[4] = kInvalidIndex;
9239 i = (MoleculeAddBonds(mp, 2, newBonds, NULL, 1) < 0 ? -1 : 0);
9240 mp->needsMDRebuild = 1;
9241 __MoleculeUnlock(mp);
9245 __MoleculeUnlock(mp);
9246 Panic("Low memory during creating dummy atoms");
9250 /* Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
9251 a bond between the two root atoms. The value bondIndex is used as a
9252 hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
9253 the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
9254 is ignored and the new bond is stored at the end of bonds[]. */
9256 MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
9263 MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
9266 if (mol == NULL || mol->noModifyTopology)
9273 __MoleculeLock(mol);
9274 NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
9275 memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
9276 mol->needsMDRebuild = 1;
9277 __MoleculeUnlock(mol);
9284 MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
9287 if (mol == NULL || mol->noModifyTopology)
9289 n1 = mol->ndihedrals;
9290 np1 = mol->dihedrals;
9291 mol->ndihedrals = 0;
9292 mol->dihedrals = NULL;
9293 if (ndihedrals > 0) {
9294 __MoleculeLock(mol);
9295 NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
9296 memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
9297 mol->needsMDRebuild = 1;
9298 __MoleculeUnlock(mol);
9300 *outDihedrals = np1;
9305 MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
9308 if (mol == NULL || mol->noModifyTopology)
9310 n1 = mol->nimpropers;
9311 np1 = mol->impropers;
9312 mol->nimpropers = 0;
9313 mol->impropers = NULL;
9314 if (nimpropers > 0) {
9315 __MoleculeLock(mol);
9316 NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
9317 memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
9318 mol->needsMDRebuild = 1;
9319 __MoleculeUnlock(mol);
9321 *outImpropers = np1;
9327 MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
9334 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
9335 return 0; /* molecule is empty */
9336 if (mol->noModifyTopology)
9340 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
9341 Int *cp = AtomConnectData(&ap->connect);
9342 if (ap->anchor != NULL)
9344 for (j = 0; j < ap->connect.count; j++) {
9346 if (ATOM_AT_INDEX(mol->atoms, j0)->anchor != NULL)
9348 for (k = j + 1; k < ap->connect.count; k++) {
9350 if (ATOM_AT_INDEX(mol->atoms, k0)->anchor != NULL)
9352 if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
9353 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
9362 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
9366 if (outAngles != NULL)
9367 *outAngles = angles;
9372 MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
9374 Int n1, n2, n3, n4, *ip, *cp2, *cp3;
9379 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
9380 return 0; /* molecule is empty */
9383 for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
9384 Int i1, i3, i4, *ip;
9385 if (ap2->anchor != NULL)
9387 cp2 = AtomConnectData(&ap2->connect);
9388 for (i3 = 0; i3 < ap2->connect.count; i3++) {
9392 ap3 = ATOM_AT_INDEX(mol->atoms, n3);
9393 if (ap3->anchor != NULL)
9395 cp3 = AtomConnectData(&ap3->connect);
9396 for (i1 = 0; i1 < ap2->connect.count; i1++) {
9400 if (ATOM_AT_INDEX(mol->atoms, n1)->anchor != NULL)
9402 for (i4 = 0; i4 < ap3->connect.count; i4++) {
9404 if (n2 == n4 || n1 == n4)
9406 if (ATOM_AT_INDEX(mol->atoms, n4)->anchor != NULL)
9408 if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
9409 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
9419 if (ndihedrals > 0) {
9420 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
9424 if (outDihedrals != NULL)
9425 *outDihedrals = dihedrals;
9430 MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
9432 Int n1, n2, n3, n4, t1, t2, t3, t4, *ip, *cp;
9433 Parameter *par = mol->par;
9438 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
9439 return 0; /* molecule is empty */
9440 if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
9441 return 0; /* No improper parameters are defined */
9445 for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
9446 Int i1, i2, i4, found, *ip;
9448 cp = AtomConnectData(&ap3->connect);
9449 for (i1 = 0; i1 < ap3->connect.count; i1++) {
9451 t1 = ATOM_AT_INDEX(ap, n1)->type;
9452 for (i2 = i1 + 1; i2 < ap3->connect.count; i2++) {
9454 t2 = ATOM_AT_INDEX(ap, n2)->type;
9455 for (i4 = i2 + 1; i4 < ap3->connect.count; i4++) {
9457 t4 = ATOM_AT_INDEX(ap, n4)->type;
9459 if (ParameterLookupImproperPar(par, t1, t2, t3, t4, n1, n2, n3, n4, 0) != NULL)
9461 else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, -1, -1, -1, -1, 0) != NULL)
9463 if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
9464 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
9474 if (nimpropers > 0) {
9475 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
9479 if (outImpropers != NULL)
9480 *outImpropers = impropers;
9484 #pragma mark ====== Residues ======
9487 MoleculeCleanUpResidueTable(Molecule *mp)
9491 if (mp == NULL || mp->natoms == 0)
9495 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9496 if (ap->resSeq >= maxres)
9497 maxres = ap->resSeq + 1;
9498 if (ap->resSeq < mp->nresidues) {
9499 if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
9500 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
9502 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
9505 if (maxres < mp->nresidues)
9506 mp->nresidues = maxres;
9507 __MoleculeUnlock(mp);
9510 /* Change the number of residues. If nresidues is greater than the current value,
9511 then the array mp->residues is expanded with null names. If nresidues is smaller
9512 than the current value, mp->nresidues is set to the smallest possible value
9513 that is no smaller than nresidues and larger than any of the resSeq values. */
9515 MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
9520 if (mp->nresidues == nresidues)
9522 else if (mp->nresidues < nresidues) {
9525 AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
9526 while (n < nresidues)
9527 mp->residues[n++][0] = 0;
9528 __MoleculeUnlock(mp);
9534 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9535 if (ap->resSeq >= n)
9544 MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
9546 IntGroupIterator iter;
9547 int withArray, resSeq, maxSeq;
9551 /* If LSB of resSeqs is 1, then a constant value is used for all specified atoms */
9552 if (((uintptr_t)resSeqs & 1) == 0) {
9557 resSeq = ((uintptr_t)resSeqs - 1) / 2;
9560 IntGroupIteratorInit(group, &iter);
9562 /* Change resSeqs */
9566 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9567 ap = ATOM_AT_INDEX(mp->atoms, i);
9569 resSeq = resSeqs[j++];
9570 if (resSeq > maxSeq)
9572 ap->resSeq = resSeq;
9574 __MoleculeUnlock(mp);
9576 /* Expand array if necessary */
9577 if (maxSeq >= mp->nresidues)
9578 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
9580 /* Synchronize resName and residues[] */
9582 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9583 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9585 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
9587 IntGroupIteratorRelease(&iter);
9588 __MoleculeUnlock(mp);
9590 MoleculeIncrementModifyCount(mp);
9596 MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
9598 return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(intptr_t)(resSeq * 2 + 1));
9601 /* Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
9602 specifies the mp->nresidues after modifying the residue numbers.
9603 If all atoms are modified, then the table of residue names is also shifted. Otherwise,
9604 the table of residue names is not touched. */
9606 MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
9608 int i, maxSeq, nmodatoms;
9610 IntGroupIterator iter;
9611 IntGroupIteratorInit(group, &iter);
9614 nresidues = mp->nresidues;
9617 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9618 ap = ATOM_AT_INDEX(mp->atoms, i);
9619 ap->resSeq += offset;
9620 if (ap->resSeq < 0) {
9621 /* Bad argument; undo change and returns this index + 1 */
9623 ap->resSeq -= offset;
9624 while ((i = IntGroupIteratorLast(&iter)) >= 0) {
9625 ap = ATOM_AT_INDEX(mp->atoms, i);
9626 ap->resSeq -= offset;
9628 IntGroupIteratorRelease(&iter);
9629 return bad_index + 1;
9631 if (ap->resSeq > maxSeq)
9632 maxSeq = ap->resSeq;
9635 if (maxSeq >= nresidues)
9636 nresidues = maxSeq + 1;
9637 if (offset < 0 && nmodatoms == mp->natoms) {
9638 /* Shift the residue names downward */
9639 memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
9641 __MoleculeUnlock(mp);
9642 MoleculeChangeNumberOfResidues(mp, nresidues);
9643 if (offset > 0 && nmodatoms == mp->natoms) {
9644 /* Shift the residue names upward */
9646 memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
9647 __MoleculeUnlock(mp);
9649 IntGroupIteratorRelease(&iter);
9651 MoleculeIncrementModifyCount(mp);
9656 /* Change residue names for the specified residue numbers. Names is an array of
9657 chars containing argc*4 characters, and every 4 characters represent a
9658 residue name; characters '\x01'-'\x1f' are converted to '\0', which allow
9659 names to be handled as a C string. */
9661 MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
9666 for (i = 0; i < argc; i++) {
9667 if (maxSeq < resSeqs[i])
9668 maxSeq = resSeqs[i];
9670 if (maxSeq >= mp->nresidues)
9671 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
9673 for (i = 0; i < argc; i++) {
9674 char *p = mp->residues[resSeqs[i]];
9676 strncpy(p, names + i * 4, 4);
9677 for (j = 0; j < 4; j++) {
9678 if (p[j] >= 0 && p[j] < 0x20)
9682 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9683 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
9685 __MoleculeUnlock(mp);
9687 MoleculeIncrementModifyCount(mp);
9692 /* Returns the maximum residue number actually used */
9694 MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
9699 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9700 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9702 if (ap->resSeq > maxSeq)
9703 maxSeq = ap->resSeq;
9708 /* Returns the minimum residue number actually used */
9710 MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
9714 minSeq = ATOMS_MAX_NUMBER;
9715 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9716 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9718 if (ap->resSeq < minSeq)
9719 minSeq = ap->resSeq;
9721 return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
9724 #pragma mark ====== Sort by Residues ======
9727 sAtomSortComparator(const void *a, const void *b)
9729 const Atom *ap, *bp;
9730 ap = *((const Atom **)a);
9731 bp = *((const Atom **)b);
9732 if (ap->resSeq == bp->resSeq) {
9733 /* Retain the original order (i.e. atom with larger pointer address is larger) */
9740 /* Compare the residue sequence. However, residue sequence 0 is always larger. */
9741 if (ap->resSeq == 0)
9743 else if (bp->resSeq == 0)
9745 else if (ap->resSeq < bp->resSeq)
9747 else if (ap->resSeq > bp->resSeq)
9754 sMoleculeReorder(Molecule *mp)
9756 int i, res, prevRes;
9760 if (mp == NULL || mp->natoms <= 1)
9763 /* Sort the atoms, bonds, etc. */
9764 apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
9765 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9766 newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9767 if (apArray == NULL || old2new == NULL || newAtoms == NULL)
9768 Panic("Low memory during reordering atoms");
9769 for (i = 0; i < mp->natoms; i++)
9770 apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
9772 /* Sort the atoms. Note: apArray is an array of "Pointer to Atom" */
9773 qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
9775 /* Make a table of 'which atom becomes which' */
9776 for (i = 0; i < mp->natoms; i++) {
9777 int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
9781 /* Renumber the bonds, etc. */
9782 for (i = 0; i < mp->nbonds * 2; i++) {
9783 mp->bonds[i] = old2new[mp->bonds[i]];
9785 for (i = 0; i < mp->nangles * 3; i++) {
9786 mp->angles[i] = old2new[mp->angles[i]];
9788 for (i = 0; i < mp->ndihedrals * 4; i++) {
9789 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9791 for (i = 0; i < mp->nimpropers * 4; i++) {
9792 mp->impropers[i] = old2new[mp->impropers[i]];
9794 for (i = 0; i < mp->natoms; i++) {
9796 ip = AtomConnectData(&(apArray[i]->connect));
9797 for (j = 0; j < apArray[i]->connect.count; j++, ip++)
9801 /* Renumber the residues so that the residue numbers are contiguous */
9803 for (i = 0; i < mp->natoms; i++) {
9804 if (apArray[i]->resSeq == 0)
9806 if (apArray[i]->resSeq != prevRes) {
9808 prevRes = apArray[i]->resSeq;
9809 if (prevRes != res) {
9810 strncpy(mp->residues[res], mp->residues[prevRes], 4);
9813 apArray[i]->resSeq = res;
9815 mp->nresidues = res + 1;
9817 /* Sort the atoms and copy back to atoms[] */
9818 for (i = 0; i < mp->natoms; i++) {
9819 memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
9821 memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
9823 /* Free the locally allocated storage */
9829 /* Renumber atoms */
9831 MoleculeRenumberAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
9833 Int *old2new, i, j, retval;
9837 if (mp->noModifyTopology)
9839 if (old2new_out != NULL)
9840 old2new = old2new_out;
9842 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9843 saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9844 if (old2new == NULL || saveAtoms == NULL)
9845 Panic("Low memory during reordering atoms");
9846 memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
9848 for (i = 0; i < mp->natoms; i++)
9850 for (i = 0; i < isize && i < mp->natoms; i++) {
9852 if (j < 0 || j >= mp->natoms) {
9853 retval = 1; /* Out of range */
9856 if (old2new[j] != -1) {
9857 retval = 2; /* Duplicate entry */
9862 if (i < mp->natoms) {
9863 for (j = 0; j < mp->natoms; j++) {
9864 if (old2new[j] != -1)
9869 if (i != mp->natoms) {
9870 retval = 3; /* Internal inconsistency */
9874 /* Renumber the bonds, etc. */
9875 for (i = 0; i < mp->nbonds * 2; i++) {
9876 mp->bonds[i] = old2new[mp->bonds[i]];
9878 for (i = 0; i < mp->nangles * 3; i++) {
9879 mp->angles[i] = old2new[mp->angles[i]];
9881 for (i = 0; i < mp->ndihedrals * 4; i++) {
9882 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9884 for (i = 0; i < mp->nimpropers * 4; i++) {
9885 mp->impropers[i] = old2new[mp->impropers[i]];
9887 /* Renumber the connection table and pi anchor table */
9888 for (i = 0; i < mp->natoms; i++) {
9889 Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
9890 Int *ip = AtomConnectData(&ap->connect);
9891 for (j = 0; j < ap->connect.count; j++, ip++)
9893 if (ap->anchor != NULL) {
9894 ip = AtomConnectData(&ap->anchor->connect);
9895 for (j = 0; j < ap->anchor->connect.count; j++, ip++)
9900 if (mp->par != NULL) {
9901 /* Renumber the parameters */
9903 for (j = kFirstParType; j <= kLastParType; j++) {
9904 n = ParameterGetCountForType(mp->par, j);
9905 for (i = 0; i < n; i++) {
9906 UnionPar *up = ParameterGetUnionParFromTypeAndIndex(mp->par, j, i);
9908 ParameterRenumberAtoms(j, up, mp->natoms, old2new);
9913 /* Renumber the atoms */
9914 for (i = 0; i < mp->natoms; i++)
9915 memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
9918 MoleculeIncrementModifyCount(mp);
9919 mp->needsMDRebuild = 1;
9922 __MoleculeUnlock(mp);
9924 if (old2new_out == NULL)
9929 #pragma mark ====== Coordinate Transform ======
9932 MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
9937 Transform rtr, symtr;
9938 if (mp == NULL || tr == NULL)
9940 TransformInvert(rtr, tr);
9942 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9943 if (group == NULL || IntGroupLookup(group, i, NULL) != 0) {
9944 TransformVec(&ap->r, tr, &ap->r);
9945 if (!SYMOP_ALIVE(ap->symop))
9947 /* Transform symop */
9948 if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9950 TransformMul(symtr, tr, symtr);
9951 if (group == NULL || IntGroupLookup(group, ap->symbase, NULL) != 0)
9952 TransformMul(symtr, symtr, rtr);
9954 if (!SYMOP_ALIVE(ap->symop))
9956 /* Transform symop if the base atom is transformed */
9957 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
9959 if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9961 TransformMul(symtr, symtr, rtr);
9963 if (MoleculeGetSymopForTransform(mp, symtr, &new_symop, 1) != 0)
9965 ap->symop = new_symop;
9967 mp->needsMDCopyCoordinates = 1;
9968 __MoleculeUnlock(mp);
9969 sMoleculeNotifyChangeAppearance(mp);
9974 MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
9978 if (mp == NULL || tr == NULL)
9981 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9982 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9984 TransformVec(&ap->r, tr, &ap->r);
9986 mp->needsMDCopyCoordinates = 1;
9987 __MoleculeUnlock(mp);
9988 sMoleculeNotifyChangeAppearance(mp);
9993 MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
9996 if (mp == NULL || vp == NULL)
9998 memset(tr, 0, sizeof(tr));
9999 tr[0] = tr[4] = tr[8] = 1.0;
10003 MoleculeTransform(mp, tr, group);
10007 MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
10010 TransformForRotation(tr, axis, angle, center);
10011 MoleculeTransform(mp, tr, group);
10015 MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
10020 if (mp == NULL || center == NULL)
10022 if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
10023 return 2; /* Empty molecule */
10025 center->x = center->y = center->z = 0.0;
10026 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10027 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
10029 VecScaleInc(*center, ap->r, ap->weight);
10033 return 3; /* Atomic weights are not defined? */
10035 VecScaleSelf(*center, w);
10040 MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
10047 if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
10048 return 2; /* Empty molecule */
10049 vmin.x = vmin.y = vmin.z = 1e50;
10050 vmax.x = vmax.y = vmax.z = -1e50;
10051 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10052 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
10054 if (vmin.x > ap->r.x)
10056 if (vmin.y > ap->r.y)
10058 if (vmin.z > ap->r.z)
10060 if (vmax.x < ap->r.x)
10062 if (vmax.y < ap->r.y)
10064 if (vmax.z < ap->r.z)
10074 #pragma mark ====== Measurements ======
10077 MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
10080 /* if (mp->is_xtal_coord) {
10081 TransformVec(&r1, mp->cell->tr, vp1);
10082 TransformVec(&r2, mp->cell->tr, vp2);
10088 return VecLength(r1);
10092 MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
10096 /* if (mp->is_xtal_coord) {
10097 TransformVec(&r1, mp->cell->tr, vp1);
10098 TransformVec(&r2, mp->cell->tr, vp2);
10099 TransformVec(&r3, mp->cell->tr, vp3);
10107 w = VecLength(r1) * VecLength(r3);
10110 return acos(VecDot(r1, r3) / w) * kRad2Deg;
10114 MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
10116 Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
10118 /* if (mp->is_xtal_coord) {
10119 TransformVec(&r1, mp->cell->tr, vp1);
10120 TransformVec(&r2, mp->cell->tr, vp2);
10121 TransformVec(&r3, mp->cell->tr, vp3);
10122 TransformVec(&r4, mp->cell->tr, vp4);
10129 VecSub(r21, r1, r2);
10130 VecSub(r32, r2, r3);
10131 VecSub(r43, r3, r4);
10132 VecCross(v1, r21, r32);
10133 VecCross(v2, r32, r43);
10134 VecCross(v3, r32, v1);
10135 w1 = VecLength(v1);
10136 w2 = VecLength(v2);
10137 w3 = VecLength(v3);
10138 if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
10144 VecScaleSelf(v1, w1);
10145 VecScaleSelf(v2, w2);
10146 VecScaleSelf(v3, w3);
10147 return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * kRad2Deg;
10151 #pragma mark ====== XtalCell Parameters ======
10154 MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
10156 if (mp->cell != NULL) {
10157 TransformVec(dst, mp->cell->tr, src);
10158 } else *dst = *src;
10162 MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
10164 if (mp->cell != NULL) {
10165 TransformVec(dst, mp->cell->rtr, src);
10166 } else *dst = *src;
10170 MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
10172 static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
10174 Vector *vp1, *vp2, *vp3;
10179 for (n1 = 0; n1 < 3; n1++) {
10180 if (cp->flags[n1] != 0)
10184 /* All directions are non-periodic */
10185 memmove(&(cp->tr), &identityTransform, sizeof(Transform));
10186 memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
10190 vp1 = &(cp->axes[n1]);
10191 vp2 = &(cp->axes[n2]);
10192 vp3 = &(cp->axes[n3]);
10193 cp->tr[n1*3] = vp1->x;
10194 cp->tr[n1*3+1] = vp1->y;
10195 cp->tr[n1*3+2] = vp1->z;
10196 cp->tr[9] = cp->origin.x;
10197 cp->tr[10] = cp->origin.y;
10198 cp->tr[11] = cp->origin.z;
10199 if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
10200 /* 1-dimensional or 2-dimensional system */
10201 /* Create "dummy" axes, so that transforms between internal and cartesian coordinates are
10202 possible with a single matrix */
10203 if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
10204 /* 1-dimensional */
10205 static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
10206 VecCross(v1, *vp1, xvec);
10207 VecCross(v2, *vp1, yvec);
10208 if (VecLength2(v1) < VecLength2(v2))
10210 VecCross(v2, *vp1, v1);
10211 if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
10212 return -1; /* Non-regular transform */
10213 } else if (cp->flags[n2] == 0) {
10215 VecCross(v1, v2, *vp1);
10216 if (NormalizeVec(&v1, &v1))
10217 return -1; /* Non-regular transform */
10220 VecCross(v2, *vp1, v1);
10221 if (NormalizeVec(&v2, &v2))
10222 return -1; /* Non-regular transform */
10224 cp->tr[n2*3] = v1.x;
10225 cp->tr[n2*3+1] = v1.y;
10226 cp->tr[n2*3+2] = v1.z;
10227 cp->tr[n3*3] = v2.x;
10228 cp->tr[n3*3+1] = v2.y;
10229 cp->tr[n3*3+2] = v2.z;
10231 VecCross(v1, *vp1, *vp2);
10232 if (fabs(VecDot(v1, *vp3)) < 1e-7)
10233 return -1; /* Non-regular transform */
10234 cp->tr[n2*3] = vp2->x;
10235 cp->tr[n2*3+1] = vp2->y;
10236 cp->tr[n2*3+2] = vp2->z;
10237 cp->tr[n3*3] = vp3->x;
10238 cp->tr[n3*3+1] = vp3->y;
10239 cp->tr[n3*3+2] = vp3->z;
10242 if (TransformInvert(cp->rtr, cp->tr))
10243 return -1; /* Non-regular transform */
10245 /* Calculate the reciprocal cell parameters */
10246 cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[3] * cp->rtr[3] + cp->rtr[6] * cp->rtr[6]);
10247 cp->rcell[1] = sqrt(cp->rtr[1] * cp->rtr[1] + cp->rtr[4] * cp->rtr[4] + cp->rtr[7] * cp->rtr[7]);
10248 cp->rcell[2] = sqrt(cp->rtr[2] * cp->rtr[2] + cp->rtr[5] * cp->rtr[5] + cp->rtr[8] * cp->rtr[8]);
10249 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;
10250 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;
10251 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;
10254 /* Calculate a, b, c, alpha, beta, gamma */
10255 cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[1] * cp->tr[1] + cp->tr[2] * cp->tr[2]);
10256 cp->cell[1] = sqrt(cp->tr[3] * cp->tr[3] + cp->tr[4] * cp->tr[4] + cp->tr[5] * cp->tr[5]);
10257 cp->cell[2] = sqrt(cp->tr[6] * cp->tr[6] + cp->tr[7] * cp->tr[7] + cp->tr[8] * cp->tr[8]);
10258 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;
10259 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;
10260 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;
10266 MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
10274 __MoleculeLock(mp);
10275 memset(&cmat, 0, sizeof(Transform));
10276 if (mp->cell != NULL)
10277 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
10279 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
10281 if (mp->cell != NULL) {
10283 mp->needsMDRebuild = 1;
10289 cp = (XtalCell *)calloc(sizeof(XtalCell), 1);
10291 Panic("Low memory during setting cell parameters");
10293 mp->needsMDRebuild = 1;
10295 /* alpha, beta, gamma are in degree */
10299 cp->cell[3] = alpha;
10300 cp->cell[4] = beta;
10301 cp->cell[5] = gamma;
10302 if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
10303 /* c unique (hexagonal etc.) */
10304 Double cosa, cosb, sinb, cosg;
10305 cosa = cos(alpha * kDeg2Rad);
10306 cosb = cos(beta * kDeg2Rad);
10307 sinb = sin(beta * kDeg2Rad);
10308 cosg = cos(gamma * kDeg2Rad);
10309 cp->axes[0].x = a * sinb;
10311 cp->axes[0].z = a * cosb;
10312 cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
10313 cp->axes[1].z = b * cosa;
10314 cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
10320 Double cosg, sing, cosa, cosb;
10321 cosa = cos(alpha * kDeg2Rad);
10322 cosb = cos(beta * kDeg2Rad);
10323 cosg = cos(gamma * kDeg2Rad);
10324 sing = sin(gamma * kDeg2Rad);
10325 cp->axes[0].x = a * sing;
10326 cp->axes[0].y = a * cosg;
10331 cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
10332 cp->axes[2].y = c * cosa;
10333 cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
10335 cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
10336 cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
10337 MoleculeCalculateCellFromAxes(cp, 0);
10338 TransformMul(cmat, cp->tr, cmat);
10341 /* Update the coordinates (if requested) */
10342 if (convertCoordinates) {
10343 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10344 TransformVec(&(ap->r), cmat, &(ap->r));
10348 /* Update the anisotropic parameters */
10349 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10350 Aniso *anp = ap->aniso;
10352 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
10355 __MoleculeUnlock(mp);
10356 sMoleculeNotifyChangeAppearance(mp);
10360 MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x13, Double x23, const Double *sigmaptr)
10364 const Double log2 = 0.693147180559945;
10365 const Double pi22 = 19.7392088021787; /* 2*pi**2 */
10371 if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
10373 anp = mp->atoms[n1].aniso;
10374 __MoleculeLock(mp);
10376 anp = (Aniso *)calloc(sizeof(Aniso), 1);
10378 __MoleculeUnlock(mp);
10379 Panic("Low memory during setting anisotropic atom parameters");
10381 mp->atoms[n1].aniso = anp;
10384 case 1: d = 1; dx = 0.5; break;
10385 case 2: d = log2; dx = log2; break;
10386 case 3: d = log2; dx = log2 * 0.5; break;
10387 case 4: u = 1; d = 0.25; dx = 0.25; break;
10388 case 5: u = 1; d = 0.25; dx = 0.125; break;
10389 case 8: u = 1; d = pi22; dx = pi22; break;
10390 case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
10391 case 10: d = pi22; dx = pi22; break;
10392 default: d = dx = 1; break;
10394 anp->bij[0] = x11 * d;
10395 anp->bij[1] = x22 * d;
10396 anp->bij[2] = x33 * d;
10397 anp->bij[3] = x12 * dx;
10398 anp->bij[4] = x13 * dx;
10399 anp->bij[5] = x23 * dx;
10400 if (sigmaptr != NULL) {
10402 anp->bsig[0] = sigmaptr[0] * d;
10403 anp->bsig[1] = sigmaptr[1] * d;
10404 anp->bsig[2] = sigmaptr[2] * d;
10405 anp->bsig[3] = sigmaptr[3] * dx;
10406 anp->bsig[4] = sigmaptr[4] * dx;
10407 anp->bsig[5] = sigmaptr[5] * dx;
10410 anp->bsig[0] = anp->bsig[1] = anp->bsig[2] = anp->bsig[3] = anp->bsig[4] = anp->bsig[5] = 0.0;
10413 if (cp != NULL && u == 1) {
10414 anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
10415 anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
10416 anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
10417 anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
10418 anp->bij[4] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[3] * kDeg2Rad); */
10419 anp->bij[5] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[4] * kDeg2Rad); */
10420 if (sigmaptr != NULL) {
10421 anp->bsig[0] *= cp->rcell[0] * cp->rcell[0];
10422 anp->bsig[1] *= cp->rcell[1] * cp->rcell[1];
10423 anp->bsig[2] *= cp->rcell[2] * cp->rcell[2];
10424 anp->bsig[3] *= cp->rcell[0] * cp->rcell[1];
10425 anp->bsig[4] *= cp->rcell[2] * cp->rcell[0];
10426 anp->bsig[5] *= cp->rcell[1] * cp->rcell[2];
10430 /* Calculate the principal axes (in Cartesian coordinates) */
10431 /* The principal axes are the eigenvectors of matrix At(B^-1)A, where
10432 B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
10433 in which x and z are the crystal-space and cartesian coordinates. */
10434 m1[0] = anp->bij[0] / pi22;
10435 m1[4] = anp->bij[1] / pi22;
10436 m1[8] = anp->bij[2] / pi22;
10437 m1[1] = m1[3] = anp->bij[3] / pi22;
10438 m1[2] = m1[6] = anp->bij[4] / pi22;
10439 m1[5] = m1[7] = anp->bij[5] / pi22;
10440 MatrixInvert(m1, m1);
10442 memmove(m2, cp->rtr, sizeof(Mat33));
10443 MatrixMul(m1, m1, m2);
10444 MatrixTranspose(m2, m2);
10445 MatrixMul(m1, m2, m1);
10447 MatrixSymDiagonalize(m1, val, axis);
10448 for (u = 0; u < 3; u++) {
10449 anp->eigval[u] = val[u];
10451 fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
10454 val[u] = 1 / sqrt(val[u]);
10456 anp->pmat[u*3] = axis[u].x * val[u];
10457 anp->pmat[u*3+1] = axis[u].y * val[u];
10458 anp->pmat[u*3+2] = axis[u].z * val[u];
10460 __MoleculeUnlock(mp);
10463 /* Set the anisotropic parameter for atom idx according to the symop. If symop is not alive, nothing is done. */
10465 MoleculeSetAnisoBySymop(Molecule *mp, int idx)
10469 if (mp == NULL || idx < 0 || idx >= mp->natoms)
10471 ap = ATOM_AT_INDEX(mp->atoms, idx);
10472 if (!SYMOP_ALIVE(ap->symop))
10474 ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
10475 if (ap2->aniso == NULL) {
10476 if (ap->aniso != NULL) {
10482 if (ap->aniso == NULL)
10483 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
10484 if (ap->symop.sym == 0 || ap->symop.sym >= mp->nsyms) {
10485 /* Just copy the aniso parameters */
10486 memmove(ap->aniso, ap2->aniso, sizeof(Aniso));
10489 memmove(t1, SYMMETRY_AT_INDEX(mp->syms, ap->symop.sym), sizeof(Transform));
10490 t1[9] = t1[10] = t1[11] = 0.0;
10491 memset(t2, 0, sizeof(Transform));
10492 t2[0] = ap2->aniso->bij[0];
10493 t2[4] = ap2->aniso->bij[1];
10494 t2[8] = ap2->aniso->bij[2];
10495 t2[1] = t2[3] = ap2->aniso->bij[3];
10496 t2[2] = t2[6] = ap2->aniso->bij[4];
10497 t2[5] = t2[7] = ap2->aniso->bij[5];
10498 TransformMul(t2, t1, t2);
10499 TransformInvert(t1, t1);
10500 TransformMul(t2, t2, t1);
10501 MoleculeSetAniso(mp, idx, 0, t2[0], t2[4], t2[8], t2[1], t2[2], t2[5], (ap2->aniso->has_bsig ? ap2->aniso->bsig : NULL));
10505 MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic, int convertCoordinates)
10507 static Vector zeroVec = {0, 0, 0};
10514 if (mp->cell != NULL)
10515 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
10517 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
10519 if (mp->cell != NULL) {
10521 mp->needsMDRebuild = 1;
10526 memset(&b, 0, sizeof(b));
10527 b.axes[0] = (ax != NULL ? *ax : zeroVec);
10528 b.axes[1] = (ay != NULL ? *ay : zeroVec);
10529 b.axes[2] = (az != NULL ? *az : zeroVec);
10531 memmove(b.flags, periodic, 3);
10532 if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
10534 __MoleculeLock(mp);
10535 if (mp->cell == NULL) {
10536 mp->needsMDRebuild = 1;
10538 if (mp->cell->has_sigma) {
10539 /* Keep the sigma */
10541 memmove(b.cellsigma, mp->cell->cellsigma, sizeof(mp->cell->cellsigma));
10543 if ((b.flags[0] != mp->cell->flags[0]) || (b.flags[1] != mp->cell->flags[1]) || (b.flags[2] != mp->cell->flags[2])) {
10544 mp->needsMDRebuild = 1;
10548 mp->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
10549 if (mp->cell != NULL) {
10550 memmove(mp->cell, &b, sizeof(XtalCell));
10551 TransformMul(cmat, b.tr, cmat);
10552 /* Update the coordinates (if requested) */
10553 if (convertCoordinates) {
10554 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10555 TransformVec(&(ap->r), cmat, &(ap->r));
10559 /* Update the anisotropic parameters */
10560 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10561 Aniso *anp = ap->aniso;
10563 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
10567 } else n = -2; /* Out of memory */
10568 __MoleculeUnlock(mp);
10569 sMoleculeNotifyChangeAppearance(mp);
10573 #pragma mark ====== Fragment manipulation ======
10576 sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
10580 if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
10582 IntGroupAdd(result, idx, 1);
10583 ap = ATOM_AT_INDEX(mp->atoms, idx);
10584 cp = AtomConnectData(&ap->connect);
10585 for (i = 0; i < ap->connect.count; i++) {
10587 if (IntGroupLookup(result, idx2, NULL))
10589 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, idx2)->anchor != NULL)
10590 continue; /* bond between two pi_anchors is ignored */
10591 sMoleculeFragmentSub(mp, idx2, result, exatoms);
10593 if (ap->anchor != NULL) {
10594 cp = AtomConnectData(&ap->anchor->connect);
10595 for (i = 0; i < ap->anchor->connect.count; i++) {
10597 if (IntGroupLookup(result, idx2, NULL))
10599 sMoleculeFragmentSub(mp, idx2, result, exatoms);
10604 /* The molecular fragment (= interconnected atoms) containing the atom n1 and
10605 not containing the atoms in exatoms */
10607 MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
10610 if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
10612 result = IntGroupNew();
10613 sMoleculeFragmentSub(mp, n1, result, exatoms);
10617 /* The molecular fragment (= interconnected atoms) containing the atom n1 and
10618 not containing the atoms n2, n3, ... (terminated by -1) */
10620 MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
10623 IntGroup *exatoms, *result;
10624 if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
10626 exatoms = IntGroupNew();
10627 for (i = 0; i < argc; i++)
10628 IntGroupAdd(exatoms, argv[i], 1);
10629 result = IntGroupNew();
10630 sMoleculeFragmentSub(mp, n1, result, exatoms);
10631 IntGroupRelease(exatoms);
10635 /* The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
10636 not containing the atoms in exatoms */
10638 MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
10640 IntGroupIterator iter;
10643 if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
10645 IntGroupIteratorInit(inatoms, &iter);
10646 result = IntGroupNew();
10647 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
10648 sMoleculeFragmentSub(mp, i, result, exatoms);
10650 IntGroupIteratorRelease(&iter);
10654 /* Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
10655 group is bound to the rest of the molecule via only one bond.
10656 If the result is true, then the atoms belonging to the (only) bond are returned
10657 in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
10658 and n2 can be NULL, if those informations are not needed. */
10660 MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
10662 Int i, i1, i2, j, k, bond_count, nval1, nval2, *cp;
10664 if (mp == NULL || mp->natoms == 0 || group == NULL)
10665 return 0; /* Invalid arguments */
10667 for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
10668 i2 = IntGroupGetEndPoint(group, i);
10669 for (j = i1; j < i2; j++) {
10670 if (j < 0 || j >= mp->natoms)
10671 return 0; /* Invalid atom group */
10672 ap = ATOM_AT_INDEX(mp->atoms, j);
10673 cp = AtomConnectData(&ap->connect);
10674 for (k = 0; k < ap->connect.count; k++) {
10675 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, cp[k])->anchor != NULL)
10676 continue; /* Ignore bond between two pi_anchors */
10677 if (IntGroupLookup(group, cp[k], NULL) == 0) {
10681 if (bond_count > 1)
10682 return 0; /* Too many bonds */
10685 if (ap->anchor != NULL) {
10686 cp = AtomConnectData(&ap->anchor->connect);
10687 for (k = 0; k < ap->anchor->connect.count; k++) {
10688 if (IntGroupLookup(group, cp[k], NULL) == 0) {
10692 if (bond_count > 1)
10693 return 0; /* Too many bonds */
10699 if (bond_count == 1) {
10710 /* Returns non-zero if the given group is 'rotatable' in the molecule. The group
10711 is said to be 'rotatable' when either of the following conditions are met; (1)
10712 the group is detachable, or (2) the group consists of two bonded atoms that define
10713 a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
10714 (either a new IntGroup or 'group' with incremented reference count; thus the caller
10715 is responsible for releasing the returned value). */
10717 MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
10720 if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
10721 if (rotGroup != NULL) {
10722 IntGroupRetain(group);
10727 if (group != NULL && IntGroupGetCount(group) == 2) {
10728 i1 = IntGroupGetNthPoint(group, 0);
10729 i2 = IntGroupGetNthPoint(group, 1);
10730 if (MoleculeAreAtomsConnected(mp, i1, i2)) {
10731 IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
10734 i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
10736 IntGroupRelease(frag);
10737 if (rotGroup != NULL)
10741 if (rotGroup != NULL)
10743 else if (frag != NULL)
10744 IntGroupRelease(frag);
10751 #pragma mark ====== Multiple frame ======
10754 MoleculeGetNumberOfFrames(Molecule *mp)
10758 if (mp->nframes <= 0) {
10762 for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10763 if (ap->nframes > n)
10770 return mp->nframes;
10774 MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame, const Vector *inFrameCell)
10776 int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
10777 Vector *tempv, *vp;
10782 if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
10785 n_old = MoleculeGetNumberOfFrames(mp);
10786 n_new = n_old + count;
10787 last_inserted = IntGroupGetNthPoint(group, count - 1);
10788 if (n_new <= last_inserted) {
10789 exframes = last_inserted - n_new + 1; /* number of extra frames that will be silently inserted */
10791 } else exframes = 0;
10793 tempv = (Vector *)malloc(sizeof(Vector) * n_new * 4); /* "*4" for handling cells */
10797 __MoleculeLock(mp);
10799 /* Copy back the current coordinates */
10800 /* No change in the current coordinates, but the frame buffer is updated */
10801 MoleculeSelectFrame(mp, mp->cframe, 1);
10803 /* Expand ap->frames for all atoms */
10804 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10805 Int n = ap->nframes;
10806 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), n_new - 1, NULL);
10807 if (ap->frames == NULL) {
10808 __MoleculeUnlock(mp);
10811 for (j = n; j < n_new; j++)
10812 ap->frames[j] = ap->r;
10814 if (mp->cell != NULL) {
10815 j = mp->nframe_cells;
10816 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, n_new - 1, NULL);
10817 for (i = j; i < n_new; i++) {
10818 /* Set the current cell parameters to the expanded frames */
10819 mp->frame_cells[i * 4] = mp->cell->axes[0];
10820 mp->frame_cells[i * 4 + 1] = mp->cell->axes[1];
10821 mp->frame_cells[i * 4 + 2] = mp->cell->axes[2];
10822 mp->frame_cells[i * 4 + 3] = mp->cell->origin;
10826 /* Expand propvals for all properties */
10827 for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
10828 dp = (Double *)realloc(prp->propvals, sizeof(Double) * n_new);
10830 __MoleculeUnlock(mp);
10833 for (j = n_old; j < n_new; j++)
10835 prp->propvals = dp;
10838 /* group = [n0..n1-1, n2..n3-1, ...] */
10840 /* tempv[0..n0-1] <- ap[0..n0-1], s += n0,
10841 tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
10842 tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
10843 tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
10845 tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
10846 At last, s will become n_old and t will become count. */
10847 for (i = 0, ap = mp->atoms, prp = mp->molprops; i <= mp->natoms + mp->nmolprops; i++) {
10848 int s, t, ns, ne, mult;
10851 if (i == mp->natoms) {
10852 if (mp->cell == NULL || mp->frame_cells == NULL)
10854 vp = mp->frame_cells;
10856 } else if (i < mp->natoms) {
10861 dp = prp->propvals;
10863 for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10865 if (i <= mp->natoms)
10866 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (ns - ne));
10868 memmove((Double *)tempv + ne, dp + s, sizeof(Double) * (ns - ne));
10871 ne = IntGroupGetEndPoint(group, j);
10873 if (i == mp->natoms) {
10874 if (inFrameCell != NULL) {
10875 tempv[ns * 4] = inFrameCell[t * 4];
10876 tempv[ns * 4 + 1] = inFrameCell[t * 4 + 1];
10877 tempv[ns * 4 + 2] = inFrameCell[t * 4 + 2];
10878 tempv[ns * 4 + 3] = inFrameCell[t * 4 + 3];
10880 tempv[ns * 4] = mp->cell->axes[0];
10881 tempv[ns * 4 + 1] = mp->cell->axes[1];
10882 tempv[ns * 4 + 2] = mp->cell->axes[2];
10883 tempv[ns * 4 + 3] = mp->cell->origin;
10885 } else if (i < mp->natoms) {
10886 if (inFrame != NULL)
10887 tempv[ns] = inFrame[natoms * t + i];
10891 ((Double *)tempv)[ns] = 0.0;
10898 if (i <= mp->natoms)
10899 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (n_new - ne));
10901 memmove((Double *)tempv + ne, dp + s, sizeof(Double) * (n_new - ne));
10904 if (i < mp->natoms)
10905 ap->nframes = n_new;
10906 if (i <= mp->natoms) {
10907 memmove(vp, tempv, sizeof(Vector) * mult * n_new);
10908 if (i < mp->natoms) {
10909 ap->nframes = n_new;
10910 ap = ATOM_NEXT(ap);
10913 memmove(dp, (Double *)tempv, sizeof(Double) * n_new);
10918 mp->nframes = n_new;
10919 MoleculeSelectFrame(mp, last_inserted, 0);
10920 MoleculeIncrementModifyCount(mp);
10921 __MoleculeUnlock(mp);
10926 MoleculeRemoveFrames(Molecule *mp, IntGroup *inGroup, Vector *outFrame, Vector *outFrameCell)
10928 int i, count, n_new, n_old, natoms, nframes, old_count, new_cframe;
10929 Vector *tempv, *vp;
10932 IntGroup *group, *group2;
10934 if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(inGroup)) <= 0)
10937 /* outFrame[] should have enough size for Vector * natoms * group.count */
10938 memset(outFrame, 0, sizeof(Vector) * natoms * count);
10939 if (mp->cell != NULL && mp->frame_cells != NULL)
10940 memset(outFrameCell, 0, sizeof(Vector) * 4 * count);
10942 n_old = MoleculeGetNumberOfFrames(mp);
10944 return -2; /* Cannot delete last frame */
10946 group = IntGroupNew();
10947 group2 = IntGroupNewWithPoints(0, n_old, -1);
10948 IntGroupIntersect(inGroup, group2, group);
10949 IntGroupRelease(group2);
10950 count = IntGroupGetCount(group);
10951 n_new = n_old - count;
10953 IntGroupRelease(group);
10954 return -2; /* Trying to delete too many frames */
10956 tempv = (Vector *)malloc(sizeof(Vector) * n_old * 4); /* "*4" for handling cells */
10957 if (tempv == NULL) {
10958 IntGroupRelease(group);
10962 __MoleculeLock(mp);
10964 /* Copy back the current coordinates */
10965 /* No change in the current coordinates, but the frame buffer is updated */
10966 MoleculeSelectFrame(mp, mp->cframe, 1);
10968 /* Determine which frame should be selected after removal is completed */
10971 if (IntGroupLookup(group, mp->cframe, &i)) {
10972 /* cframe will be removed */
10973 n1 = IntGroupGetStartPoint(group, i) - 1;
10975 n1 = IntGroupGetEndPoint(group, i);
10976 } else n1 = mp->cframe;
10977 /* Change to that frame */
10978 MoleculeSelectFrame(mp, n1, 0);
10979 group2 = IntGroupNewFromIntGroup(group);
10980 IntGroupReverse(group2, 0, n_old);
10981 new_cframe = IntGroupLookupPoint(group2, n1);
10982 if (new_cframe < 0)
10983 return -3; /* This cannot happen */
10984 IntGroupRelease(group2);
10987 /* group = [n0..n1-1, n2..n3-1, ...] */
10989 /* tempv[0..n0-1] -> ap[0..n0-1], s += n0,
10990 tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
10991 tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
10992 tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
10994 tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
10995 At last, s will become n_new and t will become count. */
10997 for (i = 0, ap = mp->atoms, prp = mp->molprops; i <= mp->natoms + mp->nmolprops; i++) {
10998 int s, t, j, ns, ne;
11000 /* if i == mp->natoms, mp->frame_cells is handled */
11001 if (i == mp->natoms) {
11002 if (mp->cell == NULL || mp->frame_cells == NULL)
11004 mult = 4 * sizeof(Vector);
11005 vp = mp->frame_cells;
11007 } else if (i < mp->natoms) {
11008 mult = sizeof(Vector);
11011 NewArray(&ap->frames, &ap->nframes, sizeof(Vector), n_old);
11012 if (ap->frames == NULL) {
11013 __MoleculeUnlock(mp);
11018 old_count = ap->nframes;
11020 mult = sizeof(Double);
11021 vp = (Vector *)prp->propvals;
11025 /* Copy vp to tempv */
11026 memset(tempv, 0, mult * n_old);
11027 memmove(tempv, vp, mult * (old_count > n_old ? n_old : old_count));
11028 ne = ns = s = t = 0;
11029 for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
11033 memmove((char *)vp + s * mult, (char *)tempv + ne * mult, mult * (ns - ne));
11036 ne = IntGroupGetEndPoint(group, j);
11040 if (i < mp->natoms)
11041 outFrame[natoms * t + i] = tempv[ns];
11042 else if (i == mp->natoms) {
11043 if (outFrameCell != NULL) {
11044 outFrameCell[t * 4] = tempv[ns * 4];
11045 outFrameCell[t * 4 + 1] = tempv[ns * 4 + 1];
11046 outFrameCell[t * 4 + 2] = tempv[ns * 4 + 2];
11047 outFrameCell[t * 4 + 3] = tempv[ns * 4 + 3];
11055 memmove((char *)vp + s * mult, (char *)tempv + ne * mult, mult * (n_old - ne));
11058 if (i < mp->natoms)
11063 if (i < mp->natoms) {
11067 } else if (i == mp->natoms) {
11068 free(mp->frame_cells);
11069 mp->frame_cells = NULL;
11070 mp->nframe_cells = 0;
11072 prp->propvals = (Double *)realloc(prp->propvals, sizeof(Double));
11075 if (i < mp->natoms) {
11076 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), s - 1, NULL);
11078 } else if (i == mp->natoms) {
11079 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, s - 1, NULL);
11080 mp->nframe_cells = s;
11082 prp->propvals = (Double *)realloc(prp->propvals, sizeof(Double) * s);
11085 if (i < mp->natoms) {
11086 ap = ATOM_NEXT(ap);
11087 } else if (i > mp->natoms) {
11092 mp->nframes = nframes;
11094 /* Select the "last" frame; do not "copy back" the coordinates to the frame table */
11095 /* i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe); */
11096 MoleculeSelectFrame(mp, new_cframe, 0);
11098 IntGroupRelease(group);
11100 MoleculeIncrementModifyCount(mp);
11101 __MoleculeUnlock(mp);
11106 MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
11108 int i, cframe, nframes, modified;
11110 cframe = mp->cframe;
11111 nframes = MoleculeGetNumberOfFrames(mp);
11113 frame = mp->cframe;
11114 if (mp == NULL || mp->natoms == 0 || frame < 0 || frame >= nframes)
11117 __MoleculeLock(mp);
11118 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
11119 if (copyback && cframe >= 0 && cframe < ap->nframes) {
11120 /* Write the current coordinate back to the frame array */
11121 ap->frames[cframe] = ap->r;
11123 if ((frame != cframe || copyback == 0) && frame >= 0 && frame < ap->nframes) {
11124 /* Read the coordinate from the frame array */
11125 ap->r = ap->frames[frame];
11130 if (mp->cell != NULL && mp->frame_cells != NULL) {
11131 /* Write the current cell back to the frame_cells array */
11132 if (copyback && cframe >= 0) {
11133 Vector *vp = (Vector *)AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, cframe, NULL);
11134 vp[0] = mp->cell->axes[0];
11135 vp[1] = mp->cell->axes[1];
11136 vp[2] = mp->cell->axes[2];
11137 vp[3] = mp->cell->origin;
11139 /* Set the cell from the frame array */
11140 if ((frame != cframe || copyback == 0) && frame >= 0 && frame < mp->nframe_cells) {
11141 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);
11143 MoleculeAmendBySymmetry(mp, NULL, NULL, NULL);
11146 mp->cframe = frame;
11148 mp->needsMDCopyCoordinates = 1;
11149 __MoleculeUnlock(mp);
11150 sMoleculeNotifyChangeAppearance(mp);
11154 /* If molecule is multi-frame, then flush the current information to the frame buffer.
11155 Returns the number of frames. */
11157 MoleculeFlushFrames(Molecule *mp)
11159 int nframes = MoleculeGetNumberOfFrames(mp);
11161 MoleculeSelectFrame(mp, mp->cframe, 1);
11166 MoleculeReorderFrames(Molecule *mp, const Int *old_idx)
11168 Int *ip, i, j, n, nframes;
11172 if (mp == NULL || old_idx == NULL)
11174 nframes = MoleculeGetNumberOfFrames(mp);
11175 MoleculeFlushFrames(mp);
11176 ip = (Int *)malloc(sizeof(Int) * nframes);
11178 return -1; /* Out of memory */
11179 memset(ip, 0, sizeof(Int) * nframes);
11180 /* Check the argument */
11181 for (i = 0; i < nframes; i++) {
11183 if (j < 0 || j >= nframes || ip[j] != 0) {
11185 return -2; /* Bad argument */
11190 dp = (Double *)malloc(sizeof(Double) * nframes * 12);
11191 for (i = 0, ap = mp->atoms, prp = mp->molprops; i <= mp->natoms + mp->nmolprops; i++) {
11192 for (j = 0; j < nframes; j++) {
11194 if (i < mp->natoms) {
11195 ((Vector *)dp)[j] = (n < ap->nframes ? ap->frames[n] : ap->r);
11196 } else if (i == mp->natoms) {
11197 if (mp->cell != NULL) {
11198 if (n < mp->nframe_cells && mp->frame_cells != NULL)
11199 memmove(dp + j * 12, mp->frame_cells + n * 4, sizeof(Vector) * 4);
11201 ((Vector *)dp)[j * 4] = mp->cell->axes[0];
11202 ((Vector *)dp)[j * 4] = mp->cell->axes[1];
11203 ((Vector *)dp)[j * 4] = mp->cell->axes[2];
11204 ((Vector *)dp)[j * 4] = mp->cell->origin;
11208 dp[j] = prp->propvals[n];
11211 for (j = 0; j < nframes; j++) {
11212 if (i < mp->natoms) {
11213 if (ap->nframes <= j)
11214 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes - 1, NULL);
11215 ap->frames[j] = ((Vector *)dp)[j];
11216 } else if (i == mp->natoms) {
11217 if (mp->cell != NULL) {
11218 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, nframes - 1, NULL);
11219 memmove(mp->frame_cells + j * 4, dp + j * 12, sizeof(Vector) * 4);
11222 prp->propvals[j] = dp[j];
11225 if (i < mp->natoms)
11226 ap = ATOM_NEXT(ap);
11227 else if (i > mp->natoms)
11231 MoleculeSelectFrame(mp, mp->cframe, 0);
11235 #pragma mark ====== Molecule Propeties ======
11238 MoleculeCreateProperty(Molecule *mp, const char *name)
11242 for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
11243 if (strcmp(prp->propname, name) == 0)
11246 prp = (MolProp *)calloc(sizeof(MolProp), 1);
11249 prp->propname = strdup(name);
11250 if (prp->propname == NULL)
11252 i = MoleculeGetNumberOfFrames(mp);
11253 prp->propvals = (Double *)calloc(sizeof(Double), i);
11254 if (prp->propvals == NULL)
11256 AssignArray(&mp->molprops, &mp->nmolprops, sizeof(MolProp), mp->nmolprops, prp);
11258 return mp->nmolprops - 1;
11262 MoleculeLookUpProperty(Molecule *mp, const char *name)
11266 for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
11267 if (strcmp(prp->propname, name) == 0)
11274 MoleculeDeletePropertyAtIndex(Molecule *mp, int idx)
11276 if (idx >= 0 && idx < mp->nmolprops) {
11277 free(mp->molprops[idx].propname);
11278 free(mp->molprops[idx].propvals);
11279 DeleteArray(&mp->molprops, &mp->nmolprops, sizeof(MolProp), idx, 1, NULL);
11286 MoleculeSetProperty(Molecule *mp, int idx, IntGroup *ig, const Double *values)
11288 IntGroupIterator iter;
11290 if (idx < 0 || idx >= mp->nmolprops)
11292 IntGroupIteratorInit(ig, &iter);
11293 nframes = MoleculeGetNumberOfFrames(mp);
11295 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
11298 mp->molprops[idx].propvals[i] = values[n];
11301 IntGroupIteratorRelease(&iter);
11306 MoleculeGetProperty(Molecule *mp, int idx, IntGroup *ig, Double *outValues)
11308 IntGroupIterator iter;
11310 if (idx < 0 || idx >= mp->nmolprops)
11312 IntGroupIteratorInit(ig, &iter);
11313 nframes = MoleculeGetNumberOfFrames(mp);
11315 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
11318 outValues[n] = mp->molprops[idx].propvals[i];
11321 IntGroupIteratorRelease(&iter);
11325 #pragma mark ====== Pi Atoms ======
11328 sMoleculeCalculatePiAnchorPosition(Atom *ap, Atom *atoms)
11332 cp = AtomConnectData(&ap->anchor->connect);
11333 n = ap->anchor->connect.count;
11335 for (j = 0; j < n; j++) {
11336 Double w = ap->anchor->coeffs[j];
11337 ap2 = ATOM_AT_INDEX(atoms, cp[j]);
11338 VecScaleInc(ap->r, ap2->r, w);
11343 MoleculeUpdatePiAnchorPositions(Molecule *mol)
11347 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
11348 if (ap->anchor == NULL)
11350 sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
11355 MoleculeCalculatePiAnchorPosition(Molecule *mol, int idx)
11358 if (mol == NULL || idx < 0 || idx >= mol->natoms)
11360 ap = ATOM_AT_INDEX(mol->atoms, idx);
11361 if (ap->anchor == NULL)
11363 sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
11367 MoleculeSetPiAnchorList(Molecule *mol, Int idx, Int nentries, Int *entries, Double *weights, Int *nUndoActions, struct MolAction ***undoActions)
11370 Int *ip, i, j, n, *np;
11372 if (mol == NULL || idx < 0 || idx >= mol->natoms || nentries <= 1)
11373 return -1; /* Invalid argument */
11374 if (weights != NULL) {
11376 for (i = 0; i < nentries; i++) {
11377 if (weights[i] <= 0.0) {
11378 return 10; /* Weights must be positive */
11383 } else d = 1.0 / nentries;
11384 ap = ATOM_AT_INDEX(mol->atoms, idx);
11385 if (ap->anchor != NULL) {
11386 /* Already an anchor: check if bonds/angles/dihedrals have entries related to this anchor */
11387 IntGroup *bg, *ag, *dg, *ig;
11388 Int *ibuf, ibufsize;
11390 bg = ag = dg = ig = NULL;
11391 ip = AtomConnectData(&ap->anchor->connect);
11392 for (i = 0; i < ap->anchor->connect.count; i++) {
11394 for (j = 0; j < nentries; j++) {
11395 if (n == entries[j])
11398 if (j == nentries) {
11399 /* This entry will disappear: if any bond/angle/dihedral has idx-n pair, that should be removed. */
11400 for (j = 0, np = mol->bonds; j < mol->nbonds; j++, np += 2) {
11401 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[0])) {
11403 bg = IntGroupNew();
11404 IntGroupAdd(bg, j, 1);
11407 for (j = 0, np = mol->angles; j < mol->nangles; j++, np += 3) {
11408 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) ||
11409 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1])) {
11411 ag = IntGroupNew();
11412 IntGroupAdd(ag, j, 1);
11415 for (j = 0, np = mol->dihedrals; j < mol->ndihedrals; j++, np += 4) {
11416 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) || (idx == np[2] && n == np[3]) ||
11417 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[3] && n == np[2])) {
11419 dg = IntGroupNew();
11420 IntGroupAdd(dg, j, 1);
11423 for (j = 0, np = mol->impropers; j < mol->nimpropers; j++, np += 4) {
11424 if ((idx == np[0] && n == np[2]) || (idx == np[1] && n == np[2]) || (idx == np[3] && n == np[2]) ||
11425 (idx == np[2] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[2] && n == np[3])) {
11427 ig = IntGroupNew();
11428 IntGroupAdd(ig, j, 1);
11436 /* Delete impropers (with undo info) */
11437 i = IntGroupGetCount(ig);
11438 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
11439 MoleculeDeleteImpropers(mol, ibuf, ig);
11440 if (nUndoActions != NULL && undoActions != NULL) {
11441 act = MolActionNew(gMolActionAddImpropers, i * 4, ibuf, ig);
11442 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11444 IntGroupRelease(ig);
11447 /* Delete dihedrals (with undo info) */
11448 i = IntGroupGetCount(dg);
11449 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
11450 MoleculeDeleteDihedrals(mol, ibuf, dg);
11451 if (nUndoActions != NULL && undoActions != NULL) {
11452 act = MolActionNew(gMolActionAddDihedrals, i * 4, ibuf, dg);
11453 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11455 IntGroupRelease(dg);
11458 /* Delete angles (with undo info) */
11459 i = IntGroupGetCount(ag);
11460 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 3 - 1, NULL);
11461 MoleculeDeleteAngles(mol, ibuf, ag);
11462 if (nUndoActions != NULL && undoActions != NULL) {
11463 act = MolActionNew(gMolActionAddAngles, i * 3, ibuf, ag);
11464 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11466 IntGroupRelease(ag);
11469 /* Delete bonds (with undo info) */
11470 i = IntGroupGetCount(bg);
11471 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 2 - 1, NULL);
11472 MoleculeDeleteBonds(mol, ibuf, bg, NULL, NULL);
11473 if (nUndoActions != NULL && undoActions != NULL) {
11474 act = MolActionNew(gMolActionAddBondsForUndo, i * 2, ibuf, bg);
11475 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11477 IntGroupRelease(bg);
11480 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
11482 AtomConnectResize(&ap->anchor->connect, nentries);
11483 memmove(AtomConnectData(&ap->anchor->connect), entries, sizeof(Int) * nentries);
11484 AssignArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), nentries - 1, NULL);
11485 if (weights != NULL) {
11486 memmove(ap->anchor->coeffs, weights, sizeof(Double) * nentries);
11487 for (i = 0; i < nentries; i++)
11488 ap->anchor->coeffs[i] *= d; /* Normalize weight */
11490 for (i = 0; i < nentries; i++)
11491 ap->anchor->coeffs[i] = d;
11493 MoleculeCalculatePiAnchorPosition(mol, idx);
11497 #pragma mark ====== MO calculation ======
11499 /* Calculate an MO value for a single point. */
11500 /* Index is the MO number (1-based); 0 denotes "arbitrary vector" */
11501 /* tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom. */
11503 sCalcMOPoint(Molecule *mp, const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
11507 Double val, tval, *cnp, *tmpp, *mobasep, *mop;
11509 /* Cache dr and |dr|^2 */
11511 index = bset->nmos + 1;
11512 for (i = 0; i < mp->natoms; i++) {
11514 r = ATOM_AT_INDEX(mp->atoms, i)->r;
11515 tmp[i * 4] = r.x = (vp->x - r.x) * kAngstrom2Bohr;
11516 tmp[i * 4 + 1] = r.y = (vp->y - r.y) * kAngstrom2Bohr;
11517 tmp[i * 4 + 2] = r.z = (vp->z - r.z) * kAngstrom2Bohr;
11518 tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
11520 /* Iterate over all shells */
11522 mobasep = bset->mo + (index - 1) * bset->ncomps;
11523 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
11525 pp = bset->priminfos + sp->p_idx;
11526 cnp = bset->cns + sp->cn_idx;
11527 if (sp->a_idx >= mp->natoms)
11528 return 0.0; /* This may happen when molecule is edited after setting up MO info */
11529 tmpp = tmp + sp->a_idx * 4;
11530 mop = mobasep + sp->m_idx;
11531 if (sp->add_exp == 0)
11534 rn = pow(tmpp[3], sp->add_exp * 0.5);
11538 for (j = 0; j < sp->nprim; j++) {
11539 tval += *cnp++ * rn * exp(-pp->A * tmpp[3]);
11542 val += mop[0] * tval;
11548 for (j = 0; j < sp->nprim; j++) {
11549 tval = rn * exp(-pp->A * tmpp[3]);
11550 x += *cnp++ * tval;
11551 y += *cnp++ * tval;
11552 z += *cnp++ * tval;
11555 x *= mop[0] * tmpp[0];
11556 y *= mop[1] * tmpp[1];
11557 z *= mop[2] * tmpp[2];
11561 case kGTOType_SP: {
11564 for (j = 0; j < sp->nprim; j++) {
11565 tval = rn * exp(-pp->A * tmpp[3]);
11566 t += *cnp++ * tval;
11567 x += *cnp++ * tval;
11568 y += *cnp++ * tval;
11569 z += *cnp++ * tval;
11573 x *= mop[1] * tmpp[0];
11574 y *= mop[2] * tmpp[1];
11575 z *= mop[3] * tmpp[2];
11576 val += t + x + y + z;
11580 Double xx, yy, zz, xy, xz, yz;
11581 xx = yy = zz = xy = xz = yz = 0;
11582 for (j = 0; j < sp->nprim; j++) {
11583 tval = rn * exp(-pp->A * tmpp[3]);
11584 xx += *cnp++ * tval;
11585 yy += *cnp++ * tval;
11586 zz += *cnp++ * tval;
11587 xy += *cnp++ * tval;
11588 xz += *cnp++ * tval;
11589 yz += *cnp++ * tval;
11592 xx *= mop[0] * tmpp[0] * tmpp[0];
11593 yy *= mop[1] * tmpp[1] * tmpp[1];
11594 zz *= mop[2] * tmpp[2] * tmpp[2];
11595 xy *= mop[3] * tmpp[0] * tmpp[1];
11596 xz *= mop[4] * tmpp[0] * tmpp[2];
11597 yz *= mop[5] * tmpp[1] * tmpp[2];
11598 val += xx + yy + zz + xy + xz + yz;
11601 case kGTOType_D5: {
11602 Double d0, d1p, d1n, d2p, d2n;
11603 d0 = d1p = d1n = d2p = d2n = 0;
11604 for (j = 0; j < sp->nprim; j++) {
11605 tval = rn * exp(-pp->A * tmpp[3]);
11606 d0 += *cnp++ * tval;
11607 d1p += *cnp++ * tval;
11608 d1n += *cnp++ * tval;
11609 d2p += *cnp++ * tval;
11610 d2n += *cnp++ * tval;
11613 // D(0): GNC(2,a,n) * (1/2) * (3zz-rr) * r^n * exp(-a*r^2)
11614 // D(+1): GNC(2,a,n) * sqrt(3) * xz * r^n * exp(-a*r^2)
11615 // D(-1): GNC(2,a,n) * sqrt(3) * yz * r^n * exp(-a*r^2)
11616 // D(+2): GNC(2,a,n) * (sqrt(3)/2) * (xx-yy) * r^n * exp(-a*r^2)
11617 // D(-2): GNC(2,a,n) * sqrt(3) * xy * r^n * exp(-a*r^2)
11618 d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
11619 d1p *= mop[1] * tmpp[0] * tmpp[2];
11620 d1n *= mop[2] * tmpp[1] * tmpp[2];
11621 d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
11622 d2n *= mop[4] * tmpp[0] * tmpp[1];
11623 val += d0 + d1p + d1n + d2p + d2n;
11627 Double xxx, yyy, zzz, xyy, xxy, xxz, xzz, yzz, yyz, xyz;
11628 xxx = yyy = zzz = xyy = xxy = xxz = xzz = yzz = yyz = xyz = 0;
11629 for (j = 0; j < sp->nprim; j++) {
11630 tval = rn * exp(-pp->A * tmpp[3]);
11631 xxx += *cnp++ * tval;
11632 yyy += *cnp++ * tval;
11633 zzz += *cnp++ * tval;
11634 xyy += *cnp++ * tval;
11635 xxy += *cnp++ * tval;
11636 xxz += *cnp++ * tval;
11637 xzz += *cnp++ * tval;
11638 yzz += *cnp++ * tval;
11639 yyz += *cnp++ * tval;
11640 xyz += *cnp++ * tval;
11643 xxx *= mop[0] * tmpp[0] * tmpp[0] * tmpp[0];
11644 yyy *= mop[1] * tmpp[1] * tmpp[1] * tmpp[1];
11645 zzz *= mop[2] * tmpp[2] * tmpp[2] * tmpp[2];
11646 xyy *= mop[3] * tmpp[0] * tmpp[1] * tmpp[1];
11647 xxy *= mop[4] * tmpp[0] * tmpp[0] * tmpp[1];
11648 xxz *= mop[5] * tmpp[0] * tmpp[0] * tmpp[2];
11649 xzz *= mop[6] * tmpp[0] * tmpp[2] * tmpp[2];
11650 yzz *= mop[7] * tmpp[1] * tmpp[2] * tmpp[2];
11651 yyz *= mop[8] * tmpp[1] * tmpp[1] * tmpp[2];
11652 xyz *= mop[9] * tmpp[0] * tmpp[1] * tmpp[2];
11653 val += xxx + yyy + zzz + xyy + xxy + xxz + xzz + yzz + yyz + xyz;
11656 case kGTOType_F7: {
11657 Double f0, f1p, f1n, f2p, f2n, f3p, f3n;
11658 f0 = f1p = f1n = f2p = f2n = f3p = f3n = 0;
11659 for (j = 0; j < sp->nprim; j++) {
11660 tval = rn * exp(-pp->A * tmpp[3]);
11661 f0 += *cnp++ * tval;
11662 f1p += *cnp++ * tval;
11663 f1n += *cnp++ * tval;
11664 f2p += *cnp++ * tval;
11665 f2n += *cnp++ * tval;
11666 f3p += *cnp++ * tval;
11667 f3n += *cnp++ * tval;
11670 // F(0): GNC(3,a,n) * (1/2) * (5zzz-3zrr) * r^n * exp(-a*r^2)
11671 // F(+1): GNC(3,a,n) * sqrt(3/8) * (5xzz-xrr) * r^n * exp(-a*r^2)
11672 // F(-1): GNC(3,a,n) * sqrt(3/8) * (5yzz-yrr) * r^n * exp(-a*r^2)
11673 // F(+2): GNC(3,a,n) * sqrt(15/4) * (xxz-yyz) * r^n * exp(-a*r^2)
11674 // F(-2): GNC(3,a,n) * sqrt(15) * xyz * r^n * exp(-a*r^2)
11675 // F(+3): GNC(3,a,n) * sqrt(5/8) * (xxx-3xyy) * r^n * exp(-a*r^2)
11676 // F(-3): GNC(3,a,n) * sqrt(5/8) * (3xxy-yyy) * r^n * exp(-a*r^2)
11677 f0 *= mop[0] * tmpp[2] * (5 * tmpp[2] * tmpp[2] - 3 * tmpp[3]);
11678 f1p *= mop[1] * tmpp[0] * (5 * tmpp[2] * tmpp[2] - tmpp[3]);
11679 f1n *= mop[2] * tmpp[1] * (5 * tmpp[2] * tmpp[2] - tmpp[3]);
11680 f2p *= mop[3] * tmpp[2] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
11681 f2n *= mop[4] * tmpp[0] * tmpp[1] * tmpp[2];
11682 f3p *= mop[5] * tmpp[0] * (tmpp[0] * tmpp[0] - 3 * tmpp[1] * tmpp[1]);
11683 f3n *= mop[6] * tmpp[1] * (3 * tmpp[0] * tmpp[0] - tmpp[2] * tmpp[2]);
11684 val += f0 + f1p + f1n + f2p + f2n + f3p + f3n;
11688 Double xxxx, yyyy, zzzz, xxxy, xxxz, yyyx, yyyz, zzzx, zzzy, xxyy, xxzz, yyzz, xxyz, yyxz, zzxy;
11689 xxxx = yyyy = zzzz = xxxy = xxxz = yyyx = yyyz = zzzx = zzzy = xxyy = xxzz = yyzz = xxyz = yyxz = zzxy = 0;
11690 for (j = 0; j < sp->nprim; j++) {
11691 tval = rn * exp(-pp->A * tmpp[3]);
11692 xxxx += *cnp++ * tval;
11693 yyyy += *cnp++ * tval;
11694 zzzz += *cnp++ * tval;
11695 xxxy += *cnp++ * tval;
11696 xxxz += *cnp++ * tval;
11697 yyyx += *cnp++ * tval;
11698 yyyz += *cnp++ * tval;
11699 zzzx += *cnp++ * tval;
11700 zzzy += *cnp++ * tval;
11701 xxyy += *cnp++ * tval;
11702 xxzz += *cnp++ * tval;
11703 yyzz += *cnp++ * tval;
11704 xxyz += *cnp++ * tval;
11705 yyxz += *cnp++ * tval;
11706 zzxy += *cnp++ * tval;
11709 xxxx *= mop[0] * tmpp[0] * tmpp[0] * tmpp[0] * tmpp[0];
11710 yyyy *= mop[1] * tmpp[1] * tmpp[1] * tmpp[1] * tmpp[1];
11711 zzzz *= mop[2] * tmpp[2] * tmpp[2] * tmpp[2] * tmpp[2];
11712 xxxy *= mop[3] * tmpp[0] * tmpp[0] * tmpp[0] * tmpp[1];
11713 xxxz *= mop[4] * tmpp[0] * tmpp[0] * tmpp[0] * tmpp[2];
11714 yyyx *= mop[5] * tmpp[1] * tmpp[1] * tmpp[1] * tmpp[0];
11715 yyyz *= mop[6] * tmpp[1] * tmpp[1] * tmpp[1] * tmpp[2];
11716 zzzx *= mop[7] * tmpp[2] * tmpp[2] * tmpp[2] * tmpp[0];
11717 zzzy *= mop[8] * tmpp[2] * tmpp[2] * tmpp[2] * tmpp[1];
11718 xxyy *= mop[9] * tmpp[0] * tmpp[0] * tmpp[1] * tmpp[1];
11719 xxzz *= mop[10] * tmpp[0] * tmpp[0] * tmpp[2] * tmpp[2];
11720 yyzz *= mop[11] * tmpp[1] * tmpp[1] * tmpp[2] * tmpp[2];
11721 xxyz *= mop[12] * tmpp[0] * tmpp[0] * tmpp[1] * tmpp[2];
11722 yyxz *= mop[13] * tmpp[1] * tmpp[1] * tmpp[0] * tmpp[2];
11723 zzxy *= mop[14] * tmpp[2] * tmpp[2] * tmpp[0] * tmpp[1];
11724 val += xxxx + yyyy + zzzz + xxxy + xxxz + yyyx + yyyz + zzzx + zzzy + xxyy + xxzz + yyzz + xxyz + yyxz + zzxy;
11727 case kGTOType_G9: {
11728 Double g0, g1p, g1n, g2p, g2n, g3p, g3n, g4p, g4n;
11729 Double xx = tmpp[0] * tmpp[0];
11730 Double yy = tmpp[1] * tmpp[1];
11731 Double zz = tmpp[2] * tmpp[2];
11732 Double rr = tmpp[3];
11733 g0 = g1p = g1n = g2p = g2n = g3p = g3n = g4p = g4n = 0;
11734 for (j = 0; j < sp->nprim; j++) {
11735 tval = rn * exp(-pp->A * tmpp[3]);
11736 g0 += *cnp++ * tval;
11737 g1p += *cnp++ * tval;
11738 g1n += *cnp++ * tval;
11739 g2p += *cnp++ * tval;
11740 g2n += *cnp++ * tval;
11741 g3p += *cnp++ * tval;
11742 g3n += *cnp++ * tval;
11743 g4p += *cnp++ * tval;
11744 g4n += *cnp++ * tval;
11747 // G(0): GNC(4,a,n) * (1/8) * (35zzzz-30zzrr+3rrrr) * r^n * exp(-a*r^2)
11748 // G(+1): GNC(4,a,n) * sqrt(5/8) * (7xzzz-3xzrr) * r^n * exp(-a*r^2)
11749 // G(-1): GNC(4,a,n) * sqrt(5/8) * (7yzzz-3yzrr) * r^n * exp(-a*r^2)
11750 // G(+2): GNC(4,a,n) * sqrt(5/16) * (xx-yy)(7zz-rr) * r^n * exp(-a*r^2)
11751 // G(-2): GNC(4,a,n) * sqrt(5/4) * (7xyzz-xyrr) * r^n * exp(-a*r^2)
11752 // G(+3): GNC(4,a,n) * sqrt(35/8) * (xxxz-3xyyz) * r^n * exp(-a*r^2)
11753 // G(-3): GNC(4,a,n) * sqrt(35/8) * (3xxyz-yyyz) * r^n * exp(-a*r^2)
11754 // G(+4): GNC(4,a,n) * sqrt(35/64) * (xxxx-6xxyy+yyyy) * r^n * exp(-a*r^2)
11755 // G(-4): GNC(4,a,n) * sqrt(35/4) * (xxxy-xyyy) * r^n * exp(-a*r^2)
11756 g0 *= mop[0] * (35 * zz * zz - 30 * zz * rr + 3 * rr * rr);
11757 g1p *= mop[1] * tmpp[0] * tmpp[2] * (7 * zz - 3 * rr);
11758 g1n *= mop[2] * tmpp[1] * tmpp[2] * (7 * zz - 3 * rr);
11759 g2p *= mop[3] * (xx - yy) * (7 * zz - rr);
11760 g2n *= mop[4] * tmpp[0] * tmpp[1] * (7 * zz - rr);
11761 g3p *= mop[5] * tmpp[0] * tmpp[2] * (xx - 3 * yy);
11762 g3n *= mop[6] * tmpp[1] * tmpp[2] * (3 * xx - yy);
11763 g4p *= mop[7] * (xx * xx - 6 * xx * yy + yy * yy);
11764 g4n *= mop[8] * tmpp[0] * tmpp[1] * (xx - yy);
11765 val += g0 + g1p + g1n + g2p + g2n + g3p + g3n + g4p + g4n;
11773 /* Calculate one MO. The input vectors are angstrom unit (changed from bohr unit: 20140520) */
11774 /* mono is the MO number (1-based); 0 denotes "arbitrary vector" */
11776 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)
11778 int ix, iy, iz, n, nn;
11781 if (mp == NULL || mp->bset == NULL)
11783 if (mp->bset->cns == NULL) {
11784 if (sSetupGaussianCoefficients(mp->bset) != 0)
11787 if (mp->bset->natoms_bs > mp->natoms)
11788 return -3; /* Number of atoms is smaller than expected (internal error) */
11790 cp = (Cube *)calloc(sizeof(Cube), 1);
11794 cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
11795 if (cp->dp == NULL) {
11808 /* TODO: use multithread */
11809 tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms_bs * 4);
11816 for (ix = 0; ix < nx; ix++) {
11818 for (iy = 0; iy < ny; iy++) {
11819 for (iz = 0; iz < nz; iz++) {
11820 p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
11821 p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
11822 p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
11823 cp->dp[n++] = sCalcMOPoint(mp, mp->bset, mono, &p, tmp);
11825 if (callback != NULL && n - nn > 100) {
11827 if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
11831 return -2; /* User interrupt */
11838 AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
11839 return mp->bset->ncubes - 1;
11842 /* Output values are in angstrom unit (changed from bohr unit: 20140520) */
11844 MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
11847 Vector rmin, rmax, r;
11848 Double dr, dx, dy, dz;
11850 if (mp == NULL || mp->bset == NULL)
11854 rmin.x = rmin.y = rmin.z = 1e10;
11855 rmax.x = rmax.y = rmax.z = -1e10;
11856 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
11857 dr = RadiusForAtomicNumber(ap->atomicNumber);
11861 dr = dr * 3.0 + 2.0;
11862 if (rmin.x > r.x - dr)
11864 if (rmin.y > r.y - dr)
11866 if (rmin.z > r.z - dr)
11868 if (rmax.x < r.x + dr)
11870 if (rmax.y < r.y + dr)
11872 if (rmax.z < r.z + dr)
11875 dx = rmax.x - rmin.x;
11876 dy = rmax.y - rmin.y;
11877 dz = rmax.z - rmin.z;
11878 dr = pow(dx * dy * dz / npoints, 1.0/3.0);
11879 *nx = floor(dx / dr + 0.5);
11880 *ny = floor(dy / dr + 0.5);
11881 *nz = floor(dz / dr + 0.5);
11889 xp->x = yp->y = zp->z = dr;
11890 xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
11895 MoleculeGetCubeAtIndex(Molecule *mp, Int index)
11897 if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
11899 return mp->bset->cubes[index];
11903 MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
11906 if (mp == NULL || mp->bset == NULL)
11908 for (i = 0; i < mp->bset->ncubes; i++) {
11909 if (mp->bset->cubes[i]->idn == mono)
11916 MoleculeClearCubeAtIndex(Molecule *mp, Int index)
11919 if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
11921 CubeRelease(mp->bset->cubes[index]);
11923 memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
11924 if (--(mp->bset->ncubes) == 0) {
11925 free(mp->bset->cubes);
11926 mp->bset->cubes = NULL;
11928 return mp->bset->ncubes;
11932 MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
11937 if (mp == NULL || mp->bset == NULL)
11938 return -1; /* Molecule or the basis set information is empty */
11939 cp = MoleculeGetCubeAtIndex(mp, index);
11941 return -2; /* MO not yet calculated */
11942 fp = fopen(fname, "wb");
11944 return -3; /* Cannot create file */
11946 /* Comment lines */
11947 fprintf(fp, "%s MO=%d\n", comment, cp->idn);
11948 fprintf(fp, " MO coefficients\n");
11950 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms_bs),
11951 cp->origin.x * kAngstrom2Bohr, cp->origin.y * kAngstrom2Bohr, cp->origin.z * kAngstrom2Bohr);
11952 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx,
11953 cp->dx.x * kAngstrom2Bohr, cp->dx.y * kAngstrom2Bohr, cp->dx.z * kAngstrom2Bohr);
11954 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny,
11955 cp->dy.x * kAngstrom2Bohr, cp->dy.y * kAngstrom2Bohr, cp->dy.z * kAngstrom2Bohr);
11956 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz,
11957 cp->dz.x * kAngstrom2Bohr, cp->dz.y * kAngstrom2Bohr, cp->dz.z * kAngstrom2Bohr);
11959 /* Atomic information */
11960 for (i = 0; i < mp->natoms; i++) {
11961 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
11962 /* The second number should actually be the effective charge */
11963 fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber,
11964 ap->r.x * kAngstrom2Bohr, ap->r.y * kAngstrom2Bohr, ap->r.z * kAngstrom2Bohr);
11966 fprintf(fp, "%5d%5d\n", 1, 1);
11969 for (i = n = 0; i < cp->nx; i++) {
11970 for (j = 0; j < cp->ny; j++) {
11971 for (k = 0; k < cp->nz; k++) {
11972 /* On Windows, the "%e" format writes the exponent in 3 digits, but
11973 this is not standard. So we avoid using %e */
11974 Double d = cp->dp[n++];
11977 if (d >= -1.0e-90 && d <= 1.0e-90) {
11981 exponent = (int)floor(log10(fabs(d)));
11982 base = d * pow(10, -1.0 * exponent);
11984 fprintf(fp, " %8.5fe%+03d", base, exponent);
11985 /* fprintf(fp, " %12.5e", d); */
11986 if (k == cp->nz - 1 || k % 6 == 5)
11995 #pragma mark ====== Marching Cube (for isosurface) ======
11998 MoleculeClearMCube(Molecule *mol, Int nx, Int ny, Int nz, const Vector *origin, Double dx, Double dy, Double dz)
12000 MCube *mc = mol->mcube;
12002 float rgba[8] = { 1, 1, 1, 0.6, 0, 0, 1, 0.6 };
12007 free(mc->c[0].cubepoints);
12008 free(mc->c[0].triangles);
12010 free(mc->c[1].cubepoints);
12011 free(mc->c[1].triangles);
12012 memmove(rgba, mc->c[0].rgba, sizeof(float) * 4);
12013 memmove(rgba + 4, mc->c[1].rgba, sizeof(float) * 4);
12017 if (nx > 0 && ny > 0 && nz > 0) {
12018 mc = (MCube *)calloc(sizeof(MCube), 1);
12020 /* round up to nearest 4N+1 integer */
12024 mc->nx = (nx + 2) / 4 * 4 + 1;
12025 mc->ny = (ny + 2) / 4 * 4 + 1;
12026 mc->nz = (nz + 2) / 4 * 4 + 1;
12027 mc->dx = dx / mc->nx;
12028 mc->dy = dy / mc->ny;
12029 mc->dz = dz / mc->nz;
12030 mc->origin = *origin;
12031 mc->dp = (Double *)malloc(sizeof(Double) * mc->nx * mc->ny * mc->nz);
12032 if (mc->dp == NULL) {
12036 mc->radii = (Double *)calloc(sizeof(Double), mol->natoms);
12037 if (mc->radii == NULL) {
12042 mc->nradii = mol->natoms;
12043 mc->c[0].fp = (unsigned char *)calloc(sizeof(unsigned char), mc->nx * mc->ny * mc->nz);
12044 mc->c[1].fp = (unsigned char *)calloc(sizeof(unsigned char), mc->nx * mc->ny * mc->nz);
12045 if (mc->c[0].fp == NULL || mc->c[1].fp == NULL) {
12053 for (i = 0; i < mc->nx * mc->ny * mc->nz; i++) {
12054 mc->dp[i] = DBL_MAX;
12056 memmove(mc->c[0].rgba, rgba, sizeof(float) * 4);
12057 memmove(mc->c[1].rgba, rgba + 4, sizeof(float) * 4);
12060 MoleculeCallback_notifyModification(mol, 0);
12064 static int sMarchingCubeTable[256][16] = {
12065 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12066 {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12067 {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12068 {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12069 {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12070 {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12071 {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12072 {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1},
12073 {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12074 {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12075 {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12076 {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1},
12077 {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12078 {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1},
12079 {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1},
12080 {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12081 {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12082 {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12083 {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12084 {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1},
12085 {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12086 {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1},
12087 {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
12088 {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1},
12089 {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12090 {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1},
12091 {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
12092 {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1},
12093 {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1},
12094 {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1},
12095 {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1},
12096 {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1},
12097 {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12098 {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12099 {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12100 {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1},
12101 {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12102 {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
12103 {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1},
12104 {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1},
12105 {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12106 {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
12107 {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
12108 {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1},
12109 {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1},
12110 {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1},
12111 {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1},
12112 {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1},
12113 {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12114 {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
12115 {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1},
12116 {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12117 {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1},
12118 {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1},
12119 {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1},
12120 {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1},
12121 {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
12122 {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1},
12123 {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1},
12124 {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1},
12125 {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1},
12126 {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1},
12127 {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1},
12128 {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12129 {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12130 {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12131 {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12132 {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
12133 {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12134 {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1},
12135 {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1},
12136 {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1},
12137 {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12138 {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
12139 {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
12140 {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1},
12141 {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1},
12142 {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1},
12143 {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1},
12144 {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1},
12145 {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12146 {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1},
12147 {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
12148 {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1},
12149 {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1},
12150 {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1},
12151 {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1},
12152 {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1},
12153 {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
12154 {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1},
12155 {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1},
12156 {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1},
12157 {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1},
12158 {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1},
12159 {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1},
12160 {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1},
12161 {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12162 {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1},
12163 {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
12164 {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1},
12165 {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1},
12166 {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1},
12167 {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12168 {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1},
12169 {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1},
12170 {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1},
12171 {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1},
12172 {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1},
12173 {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1},
12174 {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1},
12175 {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1},
12176 {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12177 {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1},
12178 {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1},
12179 {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1},
12180 {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1},
12181 {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1},
12182 {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1},
12183 {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1},
12184 {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12185 {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1},
12186 {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1},
12187 {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1},
12188 {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1},
12189 {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1},
12190 {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12191 {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1},
12192 {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12193 {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12194 {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12195 {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12196 {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
12197 {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12198 {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
12199 {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
12200 {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1},
12201 {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12202 {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1},
12203 {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1},
12204 {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1},
12205 {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1},
12206 {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1},
12207 {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1},
12208 {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1},
12209 {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12210 {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1},
12211 {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1},
12212 {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1},
12213 {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1},
12214 {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1},
12215 {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1},
12216 {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1},
12217 {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1},
12218 {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12219 {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1},
12220 {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1},
12221 {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1},
12222 {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1},
12223 {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1},
12224 {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12225 {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12226 {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
12227 {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
12228 {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1},
12229 {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
12230 {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1},
12231 {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1},
12232 {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1},
12233 {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
12234 {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1},
12235 {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1},
12236 {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1},
12237 {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1},
12238 {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1},
12239 {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1},
12240 {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1},
12241 {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1},
12242 {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1},
12243 {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1},
12244 {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1},
12245 {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1},
12246 {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1},
12247 {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1},
12248 {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1},
12249 {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1},
12250 {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1},
12251 {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1},
12252 {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12253 {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1},
12254 {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1},
12255 {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12256 {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12257 {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12258 {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1},
12259 {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1},
12260 {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1},
12261 {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1},
12262 {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1},
12263 {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1},
12264 {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1},
12265 {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1},
12266 {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1},
12267 {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1},
12268 {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1},
12269 {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12270 {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1},
12271 {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1},
12272 {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12273 {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1},
12274 {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1},
12275 {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1},
12276 {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1},
12277 {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1},
12278 {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1},
12279 {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1},
12280 {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12281 {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1},
12282 {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1},
12283 {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1},
12284 {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1},
12285 {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1},
12286 {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12287 {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1},
12288 {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12289 {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1},
12290 {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1},
12291 {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1},
12292 {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1},
12293 {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1},
12294 {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1},
12295 {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1},
12296 {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1},
12297 {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1},
12298 {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1},
12299 {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1},
12300 {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12301 {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1},
12302 {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1},
12303 {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12304 {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12305 {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12306 {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1},
12307 {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1},
12308 {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12309 {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1},
12310 {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1},
12311 {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12312 {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12313 {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1},
12314 {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12315 {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1},
12316 {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12317 {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12318 {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12319 {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12320 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
12323 /* When estimating the boundary box of the orbital, the MO values are calculated
12324 from radial distance only, ignoring the angular part. This may lead to too small
12325 value for spherical basis set, because the real MO calculation assumes expression
12326 like (3zz-rr) * r^n * exp(-a*r^2) (for D(0)). In this case, the angular part is
12327 (3zz-rr)/rr, which can take a maximum value of 2.0 instead of 1.0. So, we need to
12328 set a multiplication factor for spherical orbitals to account for this problem. */
12329 /* [0][]: D5, [1][]: F7, [2][]: G9 */
12330 /* See sCalcMOPoint() for representation of each spherical orbitals */
12331 static double sMultiplyFactors[3][10] = {
12332 { 2.0, 1.0, 1.0, 1.0, 1.0 },
12333 { 2.0, 4.0, 4.0, 1.0, 1.0, 2.0, 2.0 },
12334 { 8.0, 4.0, 4.0, 6.0, 6.0, 2.0, 2.0, 4.0, 1.0 }
12337 /* Recalculate the MCube */
12338 /* If idn < 0, then the current grid settings and values are unchanged, and */
12339 /* only the marching cubes are regenerated. */
12341 MoleculeUpdateMCube(Molecule *mol, int idn)
12343 Int retval, step, sn, mocount;
12344 Int n, ix, iy, iz, nx, ny, nz;
12345 Int nn, iix, iiy, iiz;
12346 Int ncubepoints, c1, c2, c3;
12348 Double thres, *tmp, dd;
12354 if (mol == NULL || mol->bset == NULL || mol->mcube == NULL)
12356 if (mol->bset->cns == NULL) {
12357 if (sSetupGaussianCoefficients(mol->bset) != 0)
12360 if (mol->bset->natoms_bs > mol->natoms)
12361 return -1; /* Number of atoms is smaller than expected */
12366 Double *mobasep, *mop, mopmax;
12367 Double xmin, xmax, ymin, ymax, zmin, zmax;
12368 Double thres, rthres;
12370 /* Clear mcube values */
12371 for (ix = 0; ix < mc->nx * mc->ny * mc->nz; ix++) {
12372 mc->dp[ix] = DBL_MAX;
12373 mc->c[0].fp[ix] = 0;
12374 mc->c[1].fp[ix] = 0;
12377 /* Estimate the orbital sizes */
12378 mc->radii = (Double *)realloc(mc->radii, sizeof(Double) * mol->natoms);
12379 if (mc->radii == NULL)
12380 return -2; /* Out of memory */
12381 mc->nradii = mol->natoms;
12383 /* Reset the temporary array of radii */
12384 memset(mc->radii, 0, sizeof(Double) * mc->nradii);
12385 /* Base pointer for MO coefficients */
12386 mobasep = mol->bset->mo;
12387 mocount = 1; /* Scan for only one MO, except for total density */
12388 thres = mc->thres * 0.2; /* Use lower threshold (for margin) */
12390 mobasep += mol->bset->nmos * mol->bset->ncomps; /* Arbitrary vector */
12391 else if (mc->idn < mol->bset->nmos + 1)
12392 mobasep += (mc->idn - 1) * mol->bset->ncomps;
12394 mocount = mol->bset->nmos; /* Total density; scan for all MO coefficients */
12396 for (nn = 0; nn < mol->natoms; nn++) {
12397 /* Find the suitable 'box' for each atom */
12398 /* Start from r = atomic_radius * 10, and scale r by 0.8 until fabs(p(r)) > thres */
12399 /* Then, start from r/0.8=r*1.25, and decrease r by 0.025r until fabs(p(r)) > thres */
12403 rthres = RadiusForAtomicNumber(mol->atoms[nn].atomicNumber) * 10;
12405 /* Calculate the MO value (or total density) */
12407 for (n = 0; n < mocount; n++) {
12408 /* Iterate over all shells, skipping those unrelated to the current atom */
12409 for (ix = 0, sp = mol->bset->shells; ix < mol->bset->nshells; ix++, sp++) {
12412 if (sp->a_idx != nn)
12413 continue; /* Skip this shell */
12414 pp = mol->bset->priminfos + sp->p_idx;
12415 cnp = mol->bset->cns + sp->cn_idx;
12416 mop = mobasep + (n * mol->bset->ncomps) + sp->m_idx;
12421 case kGTOType_P: k += 1; break;
12422 case kGTOType_D: k += 2; break;
12423 case kGTOType_D5: k += 2; mp = sMultiplyFactors[0]; break;
12424 case kGTOType_F: k += 3; break;
12425 case kGTOType_F7: k += 3; mp = sMultiplyFactors[1]; break;
12426 case kGTOType_G: k += 4; break;
12427 case kGTOType_G9: k += 4; mp = sMultiplyFactors[2]; break;
12429 /* Iterate over all components and primitives */
12430 for (iy = 0; iy < sp->ncomp; iy++) {
12433 if (sp->sym == kGTOType_SP && iy > 0)
12435 for (iz = 0; iz < sp->nprim; iz++) {
12436 ddd += cnp[iz] * pow(rthres, kk) * exp(-pp[iz].A * rthres * rthres);
12440 if (mc->idn == mol->bset->nmos + 1)
12441 dd += mop[iy] * mop[iy] * ddd * ddd;
12443 dd += mop[iy] * fabs(ddd);
12447 if (fabs(dd) > thres) {
12448 /* End of this pass */
12450 dr = rthres * 0.025;
12451 rthres = rthres * 1.25;
12463 if (rthres < 0.01) {
12464 /* The orbital value is less than thres for (almost) all r */
12470 if (rthres <= 0.0) {
12471 /* This cannot happen, but just in case */
12478 mc->radii[nn] = rthres * kBohr2Angstrom;
12479 } /* end loop nn */
12480 /* Create box that encloses all atoms with radius mc->radii[nn] */
12481 xmin = ymin = zmin = 1e10;
12482 xmax = ymax = zmax = -1e10;
12483 for (ix = 0, ap = mol->atoms; ix < mol->natoms; ix++, ap = ATOM_NEXT(ap)) {
12485 dd = mc->radii[ix];
12486 atomic_rad = RadiusForAtomicNumber(ap->atomicNumber);
12488 mc->radii[ix] = atomic_rad;
12491 if (atomic_rad > dd) {
12493 mc->radii[ix] = atomic_rad;
12496 if (p.x - dd < xmin)
12498 if (p.y - dd < ymin)
12500 if (p.z - dd < zmin)
12502 if (p.x + dd > xmax)
12504 if (p.y + dd > ymax)
12506 if (p.z + dd > zmax)
12509 /* Scale the box if specified so */
12510 if (mc->expand > 0.0) {
12511 Double xexp, yexp, zexp;
12512 xexp = (xmax - xmin) * (mc->expand - 1.0) * 0.5;
12513 yexp = (ymax - ymin) * (mc->expand - 1.0) * 0.5;
12514 zexp = (zmax - zmin) * (mc->expand - 1.0) * 0.5;
12522 mc->origin.x = xmin;
12523 mc->origin.y = ymin;
12524 mc->origin.z = zmin;
12525 mc->dx = (xmax - xmin) / (mc->nx - 1);
12526 mc->dy = (ymax - ymin) / (mc->ny - 1);
12527 mc->dz = (zmax - zmin) / (mc->nz - 1);
12531 /* Temporary work area */
12532 tmp = (Double *)calloc(sizeof(Double), mol->bset->natoms_bs * 4);
12536 /* TODO: use multithread */
12543 /* Calculate points within certain distances from atoms */
12544 for (nn = 0, ap = mol->atoms; nn < mol->natoms; nn++, ap = ATOM_NEXT(ap)) {
12545 /* dd = RadiusForAtomicNumber(ap->atomicNumber);
12548 dd = dd * 1.5 + 1.0; */
12549 dd = mc->radii[nn];
12550 p.x = ap->r.x - dd - mc->origin.x;
12551 p.y = ap->r.y - dd - mc->origin.y;
12552 p.z = ap->r.z - dd - mc->origin.z;
12556 iix = c1 + ceil(dd * 2.0 / mc->dx);
12557 iiy = c2 + ceil(dd * 2.0 / mc->dy);
12558 iiz = c3 + ceil(dd * 2.0 / mc->dz);
12571 for (ix = c1; ix <= iix; ix++) {
12572 p.x = mc->origin.x + mc->dx * ix;
12573 for (iy = c2; iy <= iiy; iy++) {
12574 p.y = mc->origin.y + mc->dy * iy;
12575 for (iz = c3; iz <= iiz; iz++) {
12576 n = (ix * ny + iy) * nz + iz;
12577 if (mc->dp[n] == DBL_MAX) {
12578 p.z = mc->origin.z + mc->dz * iz;
12579 if (mc->idn == mol->bset->nmos + 1) {
12580 /* Total electron density */
12581 Int ne_alpha, ne_beta;
12583 ne_alpha = mol->bset->ne_alpha;
12584 ne_beta = mol->bset->ne_beta;
12585 if (mol->bset->rflag == 2 && ne_alpha < ne_beta) {
12586 /* ROHF case: ensure ne_alpha >= ne_beta */
12587 ne_beta = ne_alpha;
12588 ne_alpha = mol->bset->ne_beta;
12590 for (sn = 1; sn <= ne_alpha; sn++) {
12591 dd = sCalcMOPoint(mol, mol->bset, sn, &p, tmp);
12593 if (mol->bset->rflag != 0 && sn <= ne_beta)
12597 if (mol->bset->rflag == 0) {
12598 for (sn = 1; sn <= ne_beta; sn++) {
12599 dd = sCalcMOPoint(mol, mol->bset, sn + mol->bset->ncomps, &p, tmp);
12600 mc->dp[n] += dd * dd;
12604 mc->dp[n] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
12613 /* (i * step, j * step, k * step) */
12614 for (ix = 0; ix < nx; ix += step) {
12615 for (iy = 0; iy < ny; iy += step) {
12616 for (iz = 0; iz < nz; iz += step) {
12617 n = (ix * ny + iy) * nz + iz;
12618 if (mc->dp[n] == DBL_MAX) {
12619 p.x = mc->origin.x + mc->dx * ix;
12620 p.y = mc->origin.y + mc->dy * iy;
12621 p.z = mc->origin.z + mc->dz * iz;
12622 mc->dp[n] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
12629 /* Intermediate points */
12630 for (step = 4; step > 1; step /= 2) {
12632 for (sn = 0; sn <= 1; sn++) {
12634 for (ix = 0; ix < nx - 1; ix += step) {
12635 for (iy = 0; iy < ny - 1; iy += step) {
12636 for (iz = 0; iz < nz - 1; iz += step) {
12638 thres = mc->thres * (sn == 0 ? 1 : -1);
12639 n = (ix * ny + iy) * nz + iz;
12640 if (mc->dp[n] == DBL_MAX || mc->dp[n + step * (nz * (ny + 1) + 1)] == DBL_MAX)
12643 if (mc->dp[n] >= thres)
12645 /* (ix + step, iy, iz) */
12646 if (mc->dp[n + step * ny * nz] >= thres)
12648 /* (ix, iy + step, iz) */
12649 if (mc->dp[n + step * nz] >= thres)
12651 /* (ix + 4, iy + step, iz) */
12652 if (mc->dp[n + step * nz * (ny + 1)] >= thres)
12654 /* (ix, iy, iz + step) */
12655 if (mc->dp[n + step] >= thres)
12657 if (mc->dp[n + step * (ny * nz + 1)] >= thres)
12659 /* (ix, iy + step, iz + step) */
12660 if (mc->dp[n + step * (nz + 1)] >= thres)
12662 /* (ix + step, iy + step, iz + step) */
12663 if (mc->dp[n + step * (nz * (ny + 1) + 1)] >= thres)
12665 if (flags != 0 && flags != 255) {
12666 /* Calc the intermediate points */
12667 for (iix = 0; iix <= step; iix += hstep) {
12668 for (iiy = 0; iiy <= step; iiy += hstep) {
12669 for (iiz = 0; iiz <= step; iiz += hstep) {
12670 if (iix % step == 0 && iiy % step == 0 && iiz % step == 0)
12672 nn = n + (iix * ny + iiy) * nz + iiz;
12673 if (mc->dp[nn] == DBL_MAX) {
12674 p.x = mc->origin.x + mc->dx * (ix + iix);
12675 p.y = mc->origin.y + mc->dy * (iy + iiy);
12676 p.z = mc->origin.z + mc->dz * (iz + iiz);
12677 mc->dp[nn] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
12693 /* Calculate vertex positions and normal vectors */
12694 for (sn = 0; sn <= 1; sn++) {
12696 thres = mc->thres * (sn == 0 ? 1 : -1);
12698 for (ix = 0; ix < nx - 1; ix++) {
12699 for (iy = 0; iy < ny - 1; iy++) {
12700 for (iz = 0; iz < nz - 1; iz++) {
12702 nn = (ix * ny + iy) * nz + iz;
12704 if (dd0 == DBL_MAX)
12707 dd1 = mc->dp[nn + ny * nz];
12708 if (dd1 != DBL_MAX)
12709 p.x = (dd1 - dd0) / mc->dx;
12710 else if (ix > 0 && (dd1 = mc->dp[nn - ny * nz]) != DBL_MAX)
12711 p.x = (dd0 - dd1) / mc->dx;
12712 else continue; /* Cannot define gradient */
12713 dd1 = mc->dp[nn + nz];
12714 if (dd1 != DBL_MAX)
12715 p.y = (dd1 - dd0) / mc->dy;
12716 else if (iy > 0 && (dd1 = mc->dp[nn - nz]) != DBL_MAX)
12717 p.y = (dd0 - dd1) / mc->dy;
12719 dd1 = mc->dp[nn + 1];
12720 if (dd1 != DBL_MAX)
12721 p.z = (dd1 - dd0) / mc->dz;
12722 else if (iz > 0 && (dd1 = mc->dp[nn - 1]) != DBL_MAX)
12723 p.z = (dd0 - dd1) / mc->dz;
12725 NormalizeVec(&p, &p);
12727 if (n + 3 >= mc->c[sn].ncubepoints) {
12728 /* Expand cubepoints[] array */
12729 mc->c[sn].cubepoints = (MCubePoint *)realloc(mc->c[sn].cubepoints, sizeof(MCubePoint) * (mc->c[sn].ncubepoints + 8192));
12730 if (mc->c[sn].cubepoints == NULL) {
12731 mc->c[sn].ncubepoints = 0;
12735 mc->c[sn].ncubepoints += 8192;
12737 mcp = mc->c[sn].cubepoints + n;
12738 iix = (dd0 >= thres ? 1 : -1);
12739 /* (x, y, z)->(x + 1, y, z) */
12740 dd1 = mc->dp[nn + ny * nz];
12741 if (dd1 != DBL_MAX) {
12742 iiy = (dd1 >= thres ? 1 : -1);
12746 mcp->d = (thres - dd0) / (dd1 - dd0);
12747 mcp->pos[0] = mc->origin.x + mc->dx * (ix + mcp->d);
12748 mcp->pos[1] = mc->origin.y + mc->dy * iy;
12749 mcp->pos[2] = mc->origin.z + mc->dz * iz;
12750 mcp->grad[0] = p.x;
12751 mcp->grad[1] = p.y;
12752 mcp->grad[2] = p.z;
12757 /* (x, y, z)->(x, y + 1, z) */
12758 dd1 = mc->dp[nn + nz];
12759 if (dd1 != DBL_MAX) {
12760 iiy = (dd1 >= thres ? 1 : -1);
12763 mcp->key = nn * 3 + 1;
12764 mcp->d = (thres - dd0) / (dd1 - dd0);
12765 mcp->pos[0] = mc->origin.x + mc->dx * ix;
12766 mcp->pos[1] = mc->origin.y + mc->dy * (iy + mcp->d);
12767 mcp->pos[2] = mc->origin.z + mc->dz * iz;
12768 mcp->grad[0] = p.x;
12769 mcp->grad[1] = p.y;
12770 mcp->grad[2] = p.z;
12775 /* (x, y, z)->(x, y, z + 1) */
12776 dd1 = mc->dp[nn + 1];
12777 if (dd1 != DBL_MAX) {
12778 iiy = (dd1 >= thres ? 1 : -1);
12781 mcp->key = nn * 3 + 2;
12782 mcp->d = (thres - dd0) / (dd1 - dd0);
12783 mcp->pos[0] = mc->origin.x + mc->dx * ix;
12784 mcp->pos[1] = mc->origin.y + mc->dy * iy;
12785 mcp->pos[2] = mc->origin.z + mc->dz * (iz + mcp->d);
12786 mcp->grad[0] = p.x;
12787 mcp->grad[1] = p.y;
12788 mcp->grad[2] = p.z;
12796 if (n < mc->c[sn].ncubepoints)
12797 mc->c[sn].cubepoints[n].key = -1; /* End mark */
12799 if (ncubepoints < 3) {
12800 /* Less than 3 points: no triangles */
12801 if (mc->c[sn].ntriangles > 0)
12802 mc->c[sn].triangles[0] = -1; /* End mark */
12806 /* Create triangle table */
12808 for (ix = 0; ix < nx - 1; ix++) {
12809 for (iy = 0; iy < ny - 1; iy++) {
12810 for (iz = 0; iz < nz - 1; iz++) {
12811 nn = (ix * ny + iy) * nz + iz;
12813 if ((dd = mc->dp[nn]) == DBL_MAX)
12815 else if (dd >= thres)
12817 if ((dd = mc->dp[nn + ny * nz]) == DBL_MAX)
12819 else if (dd >= thres)
12821 if ((dd = mc->dp[nn + ny * nz + nz]) == DBL_MAX)
12823 else if (dd >= thres)
12825 if ((dd = mc->dp[nn + nz]) == DBL_MAX)
12827 else if (dd >= thres)
12829 if ((dd = mc->dp[nn + 1]) == DBL_MAX)
12831 else if (dd >= thres)
12833 if ((dd = mc->dp[nn + ny * nz + 1]) == DBL_MAX)
12835 else if (dd >= thres)
12837 if ((dd = mc->dp[nn + ny * nz + nz + 1]) == DBL_MAX)
12839 else if (dd >= thres)
12841 if ((dd = mc->dp[nn + nz + 1]) == DBL_MAX)
12843 else if (dd >= thres)
12845 for (iiy = 0; iiy < 15; iiy++) {
12846 nn = sMarchingCubeTable[iix][iiy];
12849 /* key index for edges 0-11 */
12851 case 0: iiz = (( ix * ny + iy ) * nz + iz ) * 3; break;
12852 case 1: iiz = (((ix + 1) * ny + iy ) * nz + iz ) * 3 + 1; break;
12853 case 2: iiz = (( ix * ny + iy + 1) * nz + iz ) * 3; break;
12854 case 3: iiz = (( ix * ny + iy ) * nz + iz ) * 3 + 1; break;
12855 case 4: iiz = (( ix * ny + iy ) * nz + iz + 1) * 3; break;
12856 case 5: iiz = (((ix + 1) * ny + iy ) * nz + iz + 1) * 3 + 1; break;
12857 case 6: iiz = (( ix * ny + iy + 1) * nz + iz + 1) * 3; break;
12858 case 7: iiz = (( ix * ny + iy ) * nz + iz + 1) * 3 + 1; break;
12859 case 8: iiz = (( ix * ny + iy ) * nz + iz ) * 3 + 2; break;
12860 case 9: iiz = (((ix + 1) * ny + iy ) * nz + iz ) * 3 + 2; break;
12861 case 10: iiz = (((ix + 1) * ny + iy + 1) * nz + iz ) * 3 + 2; break;
12862 case 11: iiz = (( ix * ny + iy + 1) * nz + iz ) * 3 + 2; break;
12864 /* Skip this triangle */
12865 iiy = (iiy - iiy % 3) + 2;
12869 /* Look for the key index in cubepoints */
12871 c3 = ncubepoints - 1;
12872 mcp = mc->c[sn].cubepoints;
12875 /* c1 is always less than c3 */
12876 if (c1 + 1 == c3) {
12877 /* end of search */
12878 if (mcp[c1].key == iiz) {
12880 } else if (mcp[c3].key == iiz) {
12887 c2 = (c1 + c3) / 2;
12888 w = mcp[c2].key - iiz;
12898 /* Not found: skip this triangle */
12899 iiy = (iiy - iiy % 3) + 2;
12903 if (n + 1 >= mc->c[sn].ntriangles) {
12904 /* Expand triangles[] array */
12905 mc->c[sn].triangles = (Int *)realloc(mc->c[sn].triangles, sizeof(Int) * (mc->c[sn].ntriangles + 8192));
12906 if (mc->c[sn].triangles == NULL) {
12907 mc->c[sn].ntriangles = 0;
12911 mc->c[sn].ntriangles += 8192;
12913 mc->c[sn].triangles[n] = c2;
12919 if (n < mc->c[sn].ntriangles)
12920 mc->c[sn].triangles[n] = -1; /* End mark */
12922 /* Estimate the normal vector */
12923 for (n = 0, ip = mc->c[sn].triangles; ip[n] >= 0; n += 3) {
12925 for (ix = 0; ix < 3; ix++) {
12926 mcp = &(mc->c[sn].cubepoints[ip[n + ix]]);
12927 v[ix].x = mcp->pos[0];
12928 v[ix].y = mcp->pos[1];
12929 v[ix].z = mcp->pos[2];
12931 VecDec(v[2], v[0]);
12932 VecDec(v[1], v[0]);
12933 VecCross(v[0], v[1], v[2]);
12934 NormalizeVec(v, v);
12935 for (ix = 0; ix < 3; ix++) {
12936 mcp = &(mc->c[sn].cubepoints[ip[n + ix]]);
12937 mcp->grad[0] += v[0].x;
12938 mcp->grad[1] += v[0].y;
12939 mcp->grad[2] += v[0].z;
12942 for (n = 0, mcp = mc->c[sn].cubepoints; mcp->key >= 0; mcp++) {
12943 if (mcp->grad[0] != 0.0 || mcp->grad[1] != 0.0 || mcp->grad[2] != 0.0) {
12944 dd = 1.0 / sqrt(mcp->grad[0] * mcp->grad[0] + mcp->grad[1] * mcp->grad[1] + mcp->grad[2] * mcp->grad[2]);
12945 if (mc->thres < 0.0)
12947 mcp->grad[0] *= dd;
12948 mcp->grad[1] *= dd;
12949 mcp->grad[2] *= dd;
12954 MoleculeCallback_notifyModification(mol, 0);
12958 char *MyAppCallback_getDocumentHomeDir(void);
12962 asprintf(&s, "%s/%s", MyAppCallback_getDocumentHomeDir(), "mcube_log.txt");
12963 fp = fopen(s, "w");
12966 for (n = 0; n < mc->nx * mc->ny * mc->nz; n++) {
12967 if (mc->dp[n] == DBL_MAX)
12969 if (dmax < mc->dp[n])
12971 if (dmin > mc->dp[n])
12978 dmax = 1.001 * dmax;
12979 fprintf(fp, "thres = %g = 100\n", mc->thres);
12980 for (iz = 0; iz < mc->nz; iz++) {
12981 fprintf(fp, "z = %d\n", iz);
12982 for (iy = 0; iy < mc->ny; iy++) {
12983 for (ix = 0; ix < mc->nx; ix++) {
12984 n = (ix * ny + iy) * nz + iz;
12987 fprintf(fp, " XXX ");
12989 dd = dd * 100 / mc->thres;
12992 else if (dd < -999.0)
12994 fprintf(fp, "%4d ", (int)(dd));
13002 for (sn = 0; sn <= 1; sn++) {
13003 for (n = 0; n < mc->c[sn].ncubepoints; n++) {
13004 MCubePoint *mcp = mc->c[sn].cubepoints + n;
13009 iz = nn / 3 % mc->nz;
13010 iy = nn / (3 * mc->nz) % mc->ny;
13011 ix = nn / (3 * mc->nz * mc->ny);
13012 fprintf(fp, "%c%d:[%d,%d,%d,%d] (%g,[%g,%g,%g],[%g,%g,%g])\n", (sn == 0 ? 'p' : 'P'),
13013 n, ix, iy, iz, iix,
13014 mcp->d, mcp->pos[0], mcp->pos[1], mcp->pos[2], mcp->grad[0], mcp->grad[1], mcp->grad[2]);
13016 for (n = 0; n < mc->c[sn].ntriangles; n += 3) {
13017 if (mc->c[sn].triangles[n] < 0)
13019 fprintf(fp, "%c%d:(%d,%d,%d)\n", (sn == 0 ? 't' : 'T'), n / 3,
13020 mc->c[sn].triangles[n], mc->c[sn].triangles[n + 1], mc->c[sn].triangles[n + 2]);
13030 MoleculeDeallocateMCube(MCube *mcube)
13033 free(mcube->radii);
13034 free(mcube->c[0].cubepoints);
13035 free(mcube->c[0].triangles);
13036 free(mcube->c[1].cubepoints);
13037 free(mcube->c[1].triangles);