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)
347 MoleculeFlushFrames(mp);
348 MoleculeInitWithAtoms(mp2, mp->atoms, mp->natoms);
349 if (mp->nbonds > 0) {
350 if (NewArray(&mp2->bonds, &mp2->nbonds, sizeof(Int)*2, mp->nbonds) == NULL)
352 memmove(mp2->bonds, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
354 if (mp->nangles > 0) {
355 if (NewArray(&mp2->angles, &mp2->nangles, sizeof(Int)*3, mp->nangles) == NULL)
357 memmove(mp2->angles, mp->angles, sizeof(Int) * 3 * mp->nangles);
359 if (mp->ndihedrals > 0) {
360 if (NewArray(&mp2->dihedrals, &mp2->ndihedrals, sizeof(Int)*4, mp->ndihedrals) == NULL)
362 memmove(mp2->dihedrals, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
364 if (mp->nimpropers > 0) {
365 if (NewArray(&mp2->impropers, &mp2->nimpropers, sizeof(Int)*4, mp->nimpropers) == NULL)
367 memmove(mp2->impropers, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
369 if (mp->nresidues > 0) {
370 if (NewArray(&mp2->residues, &mp2->nresidues, sizeof(mp->residues[0]), mp->nresidues) == NULL)
372 memmove(mp2->residues, mp->residues, sizeof(mp->residues[0]) * mp->nresidues);
374 if (mp->cell != NULL) {
375 mp2->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
376 memmove(mp2->cell, mp->cell, sizeof(XtalCell));
379 NewArray(&(mp2->syms), &(mp2->nsyms), sizeof(Transform), mp->nsyms);
380 memmove(mp2->syms, mp->syms, sizeof(Transform) * mp2->nsyms);
383 /* mp2->useFlexibleCell = mp->useFlexibleCell; */
384 if (mp->nframe_cells > 0) {
385 if (NewArray(&mp2->frame_cells, &mp2->nframe_cells, sizeof(Vector) * 4, mp->nframe_cells) == NULL)
387 memmove(mp2->frame_cells, mp->frame_cells, sizeof(Vector) * 4 * mp->nframe_cells);
390 /* FIXME: should bset (basis set info) and elpot be duplicated or not? */
393 mp2->par = ParameterDuplicate(mp->par);
394 if (mp->arena != NULL) {
396 md_arena_init_from_arena(mp2->arena, mp->arena);
401 Panic("Cannot allocate memory for duplicate molecule");
402 return NULL; /* Not reached */
405 /* Assign a unique name to this parameter record */
407 MoleculeSetName(Molecule *mp, const char *name)
409 ObjectSetName((Object *)mp, name, (Object *)sMoleculeRoot);
413 MoleculeGetName(Molecule *mp)
415 return ObjectGetName((Object *)mp);
419 MoleculeWithName(const char *name)
421 return (Molecule *)ObjectWithName(name, (Object *)sMoleculeRoot);
425 MoleculeSetPath(Molecule *mol, const char *fname)
428 if (mol == NULL || fname == NULL)
430 if (fname[0] == '/' || (isalpha(fname[0]) && fname[1] == ':')) {
434 cwd = getcwd(NULL, 0);
435 asprintf(&buf, "%s/%s", cwd, fname);
438 if (mol->path != NULL) {
439 if (strcmp(mol->path, buf) == 0) {
444 free((void *)(mol->path));
447 if (mol->arena != NULL) {
448 md_close_output_files(mol->arena);
453 MoleculeGetPath(Molecule *mol)
461 MoleculeRetain(Molecule *mp)
463 ObjectIncrRefCount((Object *)mp);
464 MoleculeRetainExternalObj(mp);
469 MoleculeClear(Molecule *mp)
473 if (mp->arena != NULL) {
474 md_arena_set_molecule(mp->arena, NULL);
477 if (mp->par != NULL) {
478 ParameterRelease(mp->par);
481 if (mp->bset != NULL) {
482 BasisSetRelease(mp->bset);
485 if (mp->atoms != NULL) {
487 for (i = 0; i < mp->natoms; i++)
488 AtomClean(mp->atoms + i);
493 if (mp->bonds != NULL) {
498 if (mp->angles != NULL) {
503 if (mp->dihedrals != NULL) {
505 mp->dihedrals = NULL;
508 if (mp->impropers != NULL) {
510 mp->impropers = NULL;
513 if (mp->residues != NULL) {
518 if (mp->cell != NULL) {
522 if (mp->syms != NULL) {
527 if (mp->selection != NULL) {
528 IntGroupRelease(mp->selection);
529 mp->selection = NULL;
531 if (mp->frame_cells != NULL) {
532 free(mp->frame_cells);
533 mp->frame_cells = NULL;
534 mp->nframe_cells = 0;
536 if (mp->bset != NULL) {
537 BasisSetRelease(mp->bset);
540 if (mp->mcube != NULL) {
542 free(mp->mcube->radii);
543 free(mp->mcube->c[0].cubepoints);
544 free(mp->mcube->c[0].triangles);
545 free(mp->mcube->c[1].cubepoints);
546 free(mp->mcube->c[1].triangles);
550 if (mp->par != NULL) {
551 ParameterRelease(mp->par);
554 if (mp->elpots != NULL) {
559 if (mp->path != NULL) {
560 free((void *)mp->path);
566 MoleculeRelease(Molecule *mp)
570 MoleculeReleaseExternalObj(mp);
571 if (ObjectDecrRefCount((Object *)mp) == 0) {
573 mp->mview->mol = NULL;
574 MainView_release(mp->mview);
575 ObjectDealloc((Object *)mp, (Object **)&sMoleculeRoot);
580 MoleculeExchange(Molecule *mp1, Molecule *mp2)
583 struct MainView *mview1, *mview2;
584 struct MDArena *arena1, *arena2;
585 /* mview and arena must be kept as they are */
590 /* 'natoms' is the first member to be copied */
591 int ofs = offsetof(Molecule, natoms);
592 memmove((char *)(&mp_temp) + ofs, (char *)mp1 + ofs, sizeof(Molecule) - ofs);
593 memmove((char *)mp1 + ofs, (char *)mp2 + ofs, sizeof(Molecule) - ofs);
594 memmove((char *)mp2 + ofs, (char *)(&mp_temp) + ofs, sizeof(Molecule) - ofs);
599 /* if (mp1->arena != NULL && mp1->arena->mol == mp2)
600 mp1->arena->mol = mp1;
601 if (mp1->arena != NULL && mp1->arena->xmol == mp2)
602 mp1->arena->xmol = mp1;
603 if (mp2->arena != NULL && mp2->arena->mol == mp1)
604 mp2->arena->mol = mp2;
605 if (mp2->arena != NULL && mp2->arena->xmol == mp1)
606 mp2->arena->xmol = mp2; */
609 #pragma mark ====== Mutex ======
612 MoleculeLock(Molecule *mol)
614 if (mol == NULL || mol->mutex == NULL)
616 MoleculeCallback_lockMutex(mol->mutex);
620 MoleculeUnlock(Molecule *mol)
622 if (mol == NULL || mol->mutex == NULL)
624 MoleculeCallback_unlockMutex(mol->mutex);
627 #pragma mark ====== Modify count ======
630 MoleculeIncrementModifyCount(Molecule *mp)
633 if (++(mp->modifyCount) == 1)
634 MoleculeCallback_notifyModification(mp, 0);
635 /* fprintf(stderr, "MoleculeIncrementModifyCount: %d\n", mp->modifyCount); */
640 MoleculeClearModifyCount(Molecule *mp)
644 /* fprintf(stderr, "MoleculeClearModifyCount: %d\n", mp->modifyCount); */
648 #pragma mark ====== File handling functions ======
651 guessMoleculeType(const char *fname)
655 const char *retval = NULL;
656 fp = fopen(fname, "rb");
658 memset(buf, 0, sizeof buf);
659 if (fread(buf, 1, sizeof buf - 1, fp) > 0) {
660 if (strncmp(buf, "PSF", 3) == 0)
662 else if (((p = strstr(buf, "ATOM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r'))
663 || ((p = strstr(buf, "HETATM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r')))
666 retval = "???"; /* unknown */
674 guessElement(Atom *ap)
676 int atomicNumber = -1;
677 if (ap->atomicNumber > 0)
678 atomicNumber = ap->atomicNumber;
680 atomicNumber = GuessAtomicNumber(ap->element, ap->weight);
681 if (atomicNumber <= 0 && ap->aname[0] != 0)
682 atomicNumber = GuessAtomicNumber(ap->aname, ap->weight);
684 if (atomicNumber >= 0) {
685 ap->atomicNumber = atomicNumber;
687 ap->weight = WeightForAtomicNumber(atomicNumber);
688 if (ap->element[0] == 0)
689 ElementToString(atomicNumber, ap->element);
695 sReadLineWithInterrupt(char *buf, int size, FILE *stream, int *lineNumber)
697 static int lastLineNumber = 0;
698 if (lineNumber != NULL) {
699 if (*lineNumber == 0)
701 else if (*lineNumber >= lastLineNumber + 1000) {
702 if (MyAppCallback_checkInterrupt() != 0)
703 return -1; /* User interrupt */
704 lastLineNumber = *lineNumber;
707 return ReadLine(buf, size, stream, lineNumber);
711 s_append_asprintf(char **buf, const char *fmt, ...)
717 vasprintf(&s, fmt, va);
718 len = (*buf == NULL ? 0 : strlen(*buf));
723 *buf = malloc(len + 1);
726 *buf = realloc(*buf, len + 1);
734 MoleculeLoadFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
737 if (ftype == NULL || *ftype == 0) {
739 cp = strrchr(fname, '.');
743 cp = guessMoleculeType(fname);
744 if (strcmp(cp, "???") != 0)
748 if (strcasecmp(ftype, "psf") == 0) {
749 retval = MoleculeLoadPsfFile(mp, fname, errbuf);
750 } else if (strcasecmp(ftype, "pdb") == 0) {
751 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
752 } else if (strcasecmp(ftype, "tep") == 0) {
753 retval = MoleculeLoadTepFile(mp, fname, errbuf);
754 } else if (strcasecmp(ftype, "res") == 0 || strcasecmp(ftype, "ins") == 0) {
755 retval = MoleculeLoadShelxFile(mp, fname, errbuf);
756 } else if (strcasecmp(ftype, "fchk") == 0 || strcasecmp(ftype, "fch") == 0) {
757 retval = MoleculeLoadGaussianFchkFile(mp, fname, errbuf);
759 s_append_asprintf(errbuf, "Unknown format %s", ftype);
762 /* if (retval != 0) {
763 retval = MoleculeLoadPsfFile(mp, fname, errbuf, errbufsize);
766 MoleculeSetPath(mp, fname);
771 MoleculeLoadMbsfFile(Molecule *mp, const char *fname, char **errbuf)
775 int i, j, k, err, fn, nframes, nwarnings;
781 double mview_dbuf[10];
784 char *bufp, *valp, *comp;
789 const int kUndefined = -10000000;
793 if (mp->natoms != 0 || mp->par != NULL || mp->arena != NULL) {
794 s_append_asprintf(errbuf, "The molecule must be empty");
797 fp = fopen(fname, "rb");
799 s_append_asprintf(errbuf, "Cannot open file");
802 for (i = 0; i < 10; i++)
803 mview_dbuf[i] = kUndefined;
804 for (i = 0; i < 18; i++)
805 mview_ibuf[i] = kUndefined;
810 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
811 if (strncmp(buf, "!:", 2) != 0)
812 continue; /* Skip until section header is found */
814 strsep(&bufp, " \t\n");
815 if (strcmp(buf, "!:atoms") == 0) {
816 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
821 /* idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge */
822 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) {
823 s_append_asprintf(errbuf, "line %d: coordinates cannot be read for atom %d", lineNumber, mp->natoms + 1);
826 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
827 strncpy(ap->segName, cbuf[0], 4);
828 ap->resSeq = ibuf[1];
829 strncpy(ap->resName, cbuf[1], 4);
830 strncpy(ap->aname, cbuf[2], 4);
831 ap->type = AtomTypeEncodeToUInt(cbuf[3]);
832 ap->charge = dbuf[0];
833 ap->weight = dbuf[1];
834 strncpy(ap->element, cbuf[4], 2);
835 ap->atomicNumber = ibuf[2];
836 ap->occupancy = dbuf[2];
837 ap->tempFactor = dbuf[3];
838 ap->intCharge = ibuf[3];
841 } else if (strcmp(buf, "!:atoms_symop") == 0) {
843 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
848 /* idx symop symbase */
849 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
850 s_append_asprintf(errbuf, "line %d: symmetry operations cannot be read for atom %d", lineNumber, i + 1);
853 if (i >= mp->natoms) {
854 s_append_asprintf(errbuf, "line %d: too many atomic symmetry info\n", lineNumber);
857 ap = ATOM_AT_INDEX(mp->atoms, i);
858 ap->symop.sym = ibuf[1] / 1000000;
859 ap->symop.dx = (ibuf[1] % 1000000) / 10000;
860 ap->symop.dy = (ibuf[1] % 10000) / 100;
861 ap->symop.dz = ibuf[1] % 100;
862 ap->symbase = ibuf[2];
866 } else if (strcmp(buf, "!:atoms_fix") == 0) {
868 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
873 /* idx fix_force fix_pos */
874 if (sscanf(buf, "%d %lf %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]) < 5) {
875 s_append_asprintf(errbuf, "line %d: fix atom info cannot be read for atom %d", lineNumber, i + 1);
878 if (i >= mp->natoms) {
879 s_append_asprintf(errbuf, "line %d: too many fix atom info\n", lineNumber);
882 ap = ATOM_AT_INDEX(mp->atoms, i);
883 ap->fix_force = dbuf[0];
884 ap->fix_pos.x = dbuf[1];
885 ap->fix_pos.y = dbuf[2];
886 ap->fix_pos.z = dbuf[3];
890 } else if (strcmp(buf, "!:uff_types") == 0) {
892 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
898 if (sscanf(buf, "%d %6s", &ibuf[0], cbuf[0]) < 2) {
899 s_append_asprintf(errbuf, "line %d: uff type info cannot be read for atom %d", lineNumber, i + 1);
902 if (i >= mp->natoms) {
903 s_append_asprintf(errbuf, "line %d: too many uff type info\n", lineNumber);
906 ap = ATOM_AT_INDEX(mp->atoms, i);
907 strncpy(ap->uff_type, cbuf[0], 5);
911 } else if (strcmp(buf, "!:mm_exclude") == 0) {
913 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
918 /* idx mm_exclude periodic_exclude */
919 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
920 s_append_asprintf(errbuf, "line %d: mm_exclude flags cannot be read for atom %d", lineNumber, i + 1);
923 if (i >= mp->natoms) {
924 s_append_asprintf(errbuf, "line %d: too many mm_exclude flags\n", lineNumber);
927 ap = ATOM_AT_INDEX(mp->atoms, i);
928 ap->mm_exclude = (ibuf[1] != 0);
929 ap->periodic_exclude = (ibuf[2] != 0);
933 } else if (strcmp(buf, "!:pi_anchor") == 0) {
934 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
940 if ((j = sscanf(buf, "%d %d", &ibuf[0], &ibuf[1])) < 2) {
941 s_append_asprintf(errbuf, "line %d: bad format for pi_anchor", lineNumber);
945 ap = ATOM_AT_INDEX(mp->atoms, i);
946 if (ap->anchor != NULL) {
947 s_append_asprintf(errbuf, "line %d: warning: duplicate pi_anchor entry", lineNumber);
948 AtomConnectResize(&ap->anchor->connect, 0);
949 free(ap->anchor->coeffs);
952 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
953 if (ibuf[1] < 2 || ibuf[1] >= mp->natoms) {
954 s_append_asprintf(errbuf, "line %d: bad number of components for pi_anchor", lineNumber);
957 AtomConnectResize(&ap->anchor->connect, ibuf[1]);
958 ip = AtomConnectData(&ap->anchor->connect);
959 NewArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), ibuf[1]);
961 for (i = 0; i < j; i++) {
962 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
963 s_append_asprintf(errbuf, "line %d: unexpected end of file while reading pi_anchors", lineNumber);
966 if (sscanf(buf, "%d %lf", &ibuf[0], &dbuf[0]) < 2) {
967 s_append_asprintf(errbuf, "line %d: bad format for pi_anchor", lineNumber);
970 if (ibuf[0] < 0 || ibuf[0] >= mp->natoms) {
971 s_append_asprintf(errbuf, "line %d: atom index out of range", lineNumber);
974 if (dbuf[0] <= 0.0) {
975 s_append_asprintf(errbuf, "line %d: the pi anchor weights should be positive", lineNumber);
979 ap->anchor->coeffs[i] = dbuf[0];
983 } else if (strcmp(buf, "!:positions") == 0) {
985 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
991 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) {
992 s_append_asprintf(errbuf, "line %d: atom position cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
995 if (j > 4 && nframes != 0) {
996 s_append_asprintf(errbuf, "line %d: atom position sigma can only be given for frame 0", lineNumber);
999 if (j > 4 && j != 7) {
1000 s_append_asprintf(errbuf, "line %d: atom position sigma cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
1003 if (i >= mp->natoms) {
1004 s_append_asprintf(errbuf, "line %d: too many atom position records\n", lineNumber);
1010 ap = ATOM_AT_INDEX(mp->atoms, i);
1012 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes, &v);
1014 ap->frames[0] = ap->r;
1018 ap->sigma.x = dbuf[3];
1019 ap->sigma.y = dbuf[4];
1020 ap->sigma.z = dbuf[5];
1026 mp->nframes = nframes;
1027 mp->cframe = nframes - 1;
1029 mp->nframes = mp->cframe = 0;
1032 } else if (strcmp(buf, "!:bonds") == 0) {
1033 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1038 /* from1 to1 from2 to2 from3 to3 from4 to4 */
1039 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]);
1040 if (i < 2 || i % 2 != 0) {
1041 s_append_asprintf(errbuf, "line %d: bad bond format", lineNumber);
1044 for (j = 0; j < i; j += 2) {
1046 iibuf[1] = ibuf[j + 1];
1047 if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[0] == iibuf[1]) {
1048 s_append_asprintf(errbuf, "line %d: warning: bad bond specification (%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1]);
1050 } else if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[0])->connect), iibuf[1])) {
1051 s_append_asprintf(errbuf, "line %d: warning: bond %d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1]);
1054 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, iibuf);
1055 AtomConnectInsertEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[0])->connect), -1, iibuf[1]);
1056 AtomConnectInsertEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[1])->connect), -1, iibuf[0]);
1061 } else if (strcmp(buf, "!:bond_orders") == 0) {
1062 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1068 i = sscanf(buf, "%lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]);
1070 s_append_asprintf(errbuf, "line %d: bad bond order format", lineNumber);
1073 for (j = 0; j < i; j++) {
1074 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbondOrders, &dbuf[j]);
1077 if (mp->nbondOrders > mp->nbonds) {
1078 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);
1080 mp->nbondOrders = mp->nbonds;
1081 } else if (mp->nbondOrders < mp->nbonds) {
1082 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);
1084 j = mp->nbondOrders;
1085 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbonds - 1, NULL);
1086 for (i = j; i < mp->nbonds; i++)
1087 mp->bondOrders[i] = 0.0;
1091 } else if (strcmp(buf, "!:angles") == 0) {
1092 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1097 /* a1 b1 c1 a2 b2 c2 a3 b3 c3 */
1098 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]);
1099 if (i == 0 || i % 3 != 0) {
1100 s_append_asprintf(errbuf, "line %d: bad angle format", lineNumber);
1103 for (j = 0; j < i; j += 3) {
1105 iibuf[1] = ibuf[j + 1];
1106 iibuf[2] = ibuf[j + 2];
1107 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]) {
1108 s_append_asprintf(errbuf, "line %d: warning: bad angle specification (%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1110 } else if (MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[2]) == 0) {
1111 s_append_asprintf(errbuf, "line %d: warning: angle with non-bonded atoms (%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1113 } else if (MoleculeLookupAngle(mp, iibuf[0], iibuf[1], iibuf[2]) >= 0) {
1114 s_append_asprintf(errbuf, "line %d: warning: angle %d-%d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1117 AssignArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, mp->nangles, iibuf);
1122 } else if (strcmp(buf, "!:dihedrals") == 0) {
1123 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1128 /* a1 b1 c1 d1 a2 b2 c2 d2 */
1129 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]);
1130 if (i == 0 || i % 4 != 0) {
1131 s_append_asprintf(errbuf, "line %d: bad dihedral format", lineNumber);
1134 for (j = 0; j < i; j += 4) {
1136 iibuf[1] = ibuf[j + 1];
1137 iibuf[2] = ibuf[j + 2];
1138 iibuf[3] = ibuf[j + 3];
1139 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]) {
1140 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]);
1142 } else if (MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[2]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[3]) == 0) {
1143 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]);
1145 } else if (MoleculeLookupDihedral(mp, iibuf[0], iibuf[1], iibuf[2], iibuf[3]) >= 0) {
1146 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]);
1149 AssignArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, mp->ndihedrals, iibuf);
1154 } else if (strcmp(buf, "!:impropers") == 0) {
1155 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1160 /* a1 b1 c1 d1 a2 b2 c2 d2 */
1161 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]);
1162 if (i == 0 || i % 4 != 0) {
1163 s_append_asprintf(errbuf, "line %d: bad improper format", lineNumber);
1166 for (j = 0; j < i; j += 4) {
1168 iibuf[1] = ibuf[j + 1];
1169 iibuf[2] = ibuf[j + 2];
1170 iibuf[3] = ibuf[j + 3];
1171 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]) {
1172 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]);
1174 } else if (MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[1]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[3]) == 0) {
1175 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]);
1177 } else if (MoleculeLookupImproper(mp, iibuf[0], iibuf[1], iibuf[2], iibuf[3]) >= 0) {
1178 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]);
1181 AssignArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, mp->nimpropers, iibuf);
1186 } else if (strcmp(buf, "!:xtalcell") == 0 && mp->cell == NULL) {
1187 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1192 /* a b c alpha beta gamma [sigmaflag] */
1193 if ((j = sscanf(buf, "%lf %lf %lf %lf %lf %lf %d", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5], &ibuf[0])) < 6) {
1194 s_append_asprintf(errbuf, "line %d: bad xtalcell format", lineNumber);
1197 MoleculeSetCell(mp, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], 0);
1198 if (j == 7 && ibuf[0] != 0) {
1199 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1200 s_append_asprintf(errbuf, "line %d: sigma for xtalcell are missing", lineNumber);
1203 if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1204 s_append_asprintf(errbuf,"line %d: bad xtalcell sigma format", lineNumber);
1207 if (mp->cell != NULL) {
1208 mp->cell->has_sigma = 1;
1209 for (i = 0; i < 6; i++) {
1210 mp->cell->cellsigma[i] = dbuf[i];
1213 s_append_asprintf(errbuf, "line %d: cell sigma are given while cell is not given", lineNumber);
1218 } else if (strcmp(buf, "!:symmetry_operations") == 0) {
1220 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1226 /* a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3 */
1227 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1228 s_append_asprintf(errbuf, "line %d: bad symmetry_operation format", lineNumber);
1233 tr[i + 3] = dbuf[1];
1234 tr[i + 6] = dbuf[2];
1242 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
1247 } else if (strcmp(buf, "!:anisotropic_thermal_parameters") == 0) {
1249 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1254 /* b11 b22 b33 b12 b13 b23 [has_sigma] */
1255 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) {
1256 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters cannot be read for atom %d", lineNumber, i + 1);
1259 if (i >= mp->natoms) {
1260 s_append_asprintf(errbuf, "line %d: too many anisotropic thermal parameters\n", lineNumber);
1263 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) {
1266 MoleculeSetAniso(mp, i, 0, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], NULL);
1268 if (j == 7 && ibuf[0] != 0) {
1269 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1270 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma missing", lineNumber);
1273 if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1274 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma cannot be read for atom %d", lineNumber, i + 1);
1277 ap = ATOM_AT_INDEX(mp->atoms, i);
1278 if (ap->aniso == NULL) {
1279 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma are given while the parameters are not given", lineNumber);
1282 ap->aniso->has_bsig = 1;
1283 for (j = 0; j < 6; j++)
1284 ap->aniso->bsig[j] = dbuf[j];
1289 } else if (strcmp(buf, "!:periodic_box") == 0) {
1293 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1298 /* 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] */
1300 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1301 s_append_asprintf(errbuf, "line %d: bad periodic_box format", lineNumber);
1310 if ((j = sscanf(buf, "%d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3])) < 3) {
1311 s_append_asprintf(errbuf, "line %d: bad periodic_box format", lineNumber);
1314 if (j == 4 && ibuf[3] != 0)
1316 cbuf[0][0] = ibuf[0];
1317 cbuf[0][1] = ibuf[1];
1318 cbuf[0][2] = ibuf[2];
1319 MoleculeSetPeriodicBox(mp, vs, vs + 1, vs + 2, vs + 3, cbuf[0], 0);
1321 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1322 s_append_asprintf(errbuf, "line %d: sigma for cell parameters are missing", lineNumber);
1325 if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1326 s_append_asprintf(errbuf, "line %d: bad periodic_box sigma format", lineNumber);
1329 if (mp->cell != NULL) {
1330 mp->cell->has_sigma = 1;
1331 for (i = 0; i < 6; i++) {
1332 mp->cell->cellsigma[i] = dbuf[i];
1335 s_append_asprintf(errbuf, "line %d: cell sigma are given while cell is not given", lineNumber);
1341 } else if (strcmp(buf, "!:frame_periodic_boxes") == 0) {
1344 /* mp->useFlexibleCell = 1; *//* The presence of this block causes asserting this flag */
1345 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1350 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1351 s_append_asprintf(errbuf, "line %d: bad frame_periodic_box format", lineNumber);
1359 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, mp->nframe_cells, vs);
1363 if (mp->cframe < mp->nframe_cells) {
1364 /* mp->cframe should already have been set when positions are read */
1365 Vector *vp = &mp->frame_cells[mp->cframe * 4];
1366 static char defaultFlags[] = {1, 1, 1};
1367 char *flags = (mp->cell != NULL ? mp->cell->flags : defaultFlags);
1368 MoleculeSetPeriodicBox(mp, vp, vp + 1, vp + 2, vp + 3, flags, 0);
1371 } else if (strcmp(buf, "!:md_parameters") == 0) {
1373 if (mp->arena == NULL)
1374 mp->arena = md_arena_new(NULL);
1376 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1382 comp = strsep(&bufp, " \t");
1384 while (*bufp == ' ' || *bufp == '\t')
1386 valp = strsep(&bufp, "\n");
1388 if (strcmp(comp, "alchem_flags") == 0) {
1389 j = (valp == NULL ? 0 : atoi(valp));
1391 valp = (char *)malloc(j);
1393 while ((k = fgetc(fp)) >= 0) {
1395 if (k < '0' || k > '9') {
1396 s_append_asprintf(errbuf, "line %d: too few flags in alchem_flags block", lineNumber + 1);
1400 ReadLine(buf, sizeof buf, fp, &lineNumber);
1402 while (*bufp != 0) {
1403 if (*bufp >= '0' && *bufp <= '2') {
1405 s_append_asprintf(errbuf, "line %d: too many flags in alchem_flags block", lineNumber);
1409 valp[i++] = *bufp - '0';
1410 } else if (*bufp != ' ' && *bufp != '\t' && *bufp != '\n') {
1411 s_append_asprintf(errbuf, "line %d: strange character (0x%02x) in alchem_flags block", lineNumber, (int)*bufp);
1420 md_set_alchemical_flags(arena, j, valp);
1425 /* In the following, the redundant "!= NULL" is to suppress suprious warning */
1426 if ((strcmp(comp, "log_file") == 0 && (pp = &arena->log_result_name) != NULL)
1427 || (strcmp(comp, "coord_file") == 0 && (pp = &arena->coord_result_name) != NULL)
1428 || (strcmp(comp, "vel_file") == 0 && (pp = &arena->vel_result_name) != NULL)
1429 || (strcmp(comp, "force_file") == 0 && (pp = &arena->force_result_name) != NULL)
1430 || (strcmp(comp, "debug_file") == 0 && (pp = &arena->debug_result_name) != NULL)) {
1431 if (*valp == 0 || strstr(valp, "(null)") == valp)
1434 valp = strdup(valp);
1436 char *valp1 = strchr(valp, '\n');
1442 } else if ((strcmp(comp, "debug_output_level") == 0 && (ip = &arena->debug_output_level) != NULL)
1443 || (strcmp(comp, "coord_output_freq") == 0 && (ip = &arena->coord_output_freq) != NULL)
1444 || (strcmp(comp, "energy_output_freq") == 0 && (ip = &arena->energy_output_freq) != NULL)
1445 || (strcmp(comp, "coord_frame") == 0 && (ip = &arena->coord_result_frame) != NULL)
1446 || (strcmp(comp, "andersen_freq") == 0 && (ip = &arena->andersen_thermo_freq) != NULL)
1447 || (strcmp(comp, "random_seed") == 0 && (ip = &arena->random_seed) != NULL)
1448 || (strcmp(comp, "use_xplor_shift") == 0 && (ip = &arena->use_xplor_shift) != NULL)
1449 || (strcmp(comp, "relocate_center") == 0 && (ip = &arena->relocate_center) != NULL)
1450 || (strcmp(comp, "surface_potential_freq") == 0 && (ip = &arena->surface_potential_freq) != NULL)
1451 || (strcmp(comp, "use_graphite") == 0 && (ip = &arena->use_graphite) != NULL)) {
1452 *ip = (valp == NULL ? 0 : atoi(valp));
1453 } else if ((strcmp(comp, "timestep") == 0 && (dp = &arena->timestep) != NULL)
1454 || (strcmp(comp, "cutoff") == 0 && (dp = &arena->cutoff) != NULL)
1455 || (strcmp(comp, "electro_cutoff") == 0 && (dp = &arena->electro_cutoff) != NULL)
1456 || (strcmp(comp, "pairlist_distance") == 0 && (dp = &arena->pairlist_distance) != NULL)
1457 || (strcmp(comp, "switch_distance") == 0 && (dp = &arena->switch_distance) != NULL)
1458 || (strcmp(comp, "temperature") == 0 && (dp = &arena->temperature) != NULL)
1459 || (strcmp(comp, "andersen_coupling") == 0 && (dp = &arena->andersen_thermo_coupling) != NULL)
1460 || (strcmp(comp, "dielectric") == 0 && (dp = &arena->dielectric) != NULL)
1461 || (strcmp(comp, "gradient_convergence") == 0 && (dp = &arena->gradient_convergence) != NULL)
1462 || (strcmp(comp, "coordinate_convergence") == 0 && (dp = &arena->coordinate_convergence) != NULL)
1463 || (strcmp(comp, "scale14_vdw") == 0 && (dp = &arena->scale14_vdw) != NULL)
1464 || (strcmp(comp, "scale14_elect") == 0 && (dp = &arena->scale14_elect) != NULL)
1465 || (strcmp(comp, "surface_probe_radius") == 0 && (dp = &arena->probe_radius) != NULL)
1466 || (strcmp(comp, "surface_tension") == 0 && (dp = &arena->surface_tension) != NULL)
1467 || (strcmp(comp, "alchemical_lambda") == 0 && (dp = &arena->alchem_lambda) != NULL)
1468 || (strcmp(comp, "alchemical_delta_lambda") == 0 && (dp = &arena->alchem_dlambda) != NULL)) {
1469 *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1473 } else if (strcmp(buf, "!:pressure_control_parameters") == 0) {
1474 MDPressureArena *pressure;
1475 if (mp->arena == NULL)
1476 mp->arena = md_arena_new(mp);
1477 if (mp->arena->pressure == NULL)
1478 mp->arena->pressure = pressure_new();
1479 pressure = mp->arena->pressure;
1480 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1486 comp = strsep(&bufp, " \t");
1488 while (*bufp == ' ' || *bufp == '\t')
1490 valp = strsep(&bufp, "\n");
1492 if (strcmp(comp, "pressure") == 0) {
1493 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) {
1494 s_append_asprintf(errbuf, "line %d: bad format", lineNumber);
1497 for (i = 0; i < 9; i++)
1498 pressure->apply[i] = dbuf[i];
1499 } else if (strcmp(comp, "pressure_cell_flexibility") == 0) {
1500 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) {
1501 s_append_asprintf(errbuf, "line %d: bad format", lineNumber);
1504 for (i = 0; i < 8; i++)
1505 pressure->cell_flexibility[i] = dbuf[i];
1506 } else if ((strcmp(comp, "pressure_freq") == 0 && (ip = &pressure->freq) != NULL)) {
1507 *ip = (valp == NULL ? 0 : atoi(valp));
1508 } else if ((strcmp(comp, "pressure_coupling") == 0 && (dp = &pressure->coupling) != NULL)
1509 || (strcmp(comp, "pressure_fluctuate_cell_origin") == 0 && (dp = &pressure->fluctuate_cell_origin) != NULL)
1510 || (strcmp(comp, "pressure_fluctuate_cell_orientation") == 0 && (dp = &pressure->fluctuate_cell_orientation) != NULL)) {
1511 *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1515 } else if (strcmp(buf, "!:velocity") == 0) {
1517 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1523 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1524 s_append_asprintf(errbuf, "line %d: atom velocity cannot be read for atom %d", lineNumber, i + 1);
1527 if (i >= mp->natoms) {
1528 s_append_asprintf(errbuf, "line %d: too many atom velocity records\n", lineNumber);
1531 ap = ATOM_AT_INDEX(mp->atoms, i);
1538 } else if (strcmp(buf, "!:force") == 0) {
1540 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1546 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1547 s_append_asprintf(errbuf, "line %d: atom force cannot be read for atom %d", lineNumber, i + 1);
1550 if (i >= mp->natoms) {
1551 s_append_asprintf(errbuf, "line %d: too many atom force records\n", lineNumber);
1554 ap = ATOM_AT_INDEX(mp->atoms, i);
1561 } else if (strcmp(buf, "!:parameter") == 0 || strcmp(buf, "!:parameters") == 0) {
1562 Parameter *par = mp->par;
1564 mp->par = ParameterNew();
1569 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1574 j = ParameterReadFromString(par, buf, &bufp, fname, lineNumber, 0);
1576 s_append_asprintf(errbuf, "%s", bufp);
1583 s_append_asprintf(errbuf, "%s", bufp);
1587 } else if (strcmp(buf, "!:trackball") == 0) {
1589 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1594 /* scale; trx try trz; theta_deg x y z */
1595 if ((i == 0 && sscanf(buf, "%lf", &mview_dbuf[0]) < 1)
1596 || (i == 1 && sscanf(buf, "%lf %lf %lf",
1597 &mview_dbuf[1], &mview_dbuf[2], &mview_dbuf[3]) < 3)
1598 || (i == 2 && sscanf(buf, "%lf %lf %lf %lf",
1599 &mview_dbuf[4], &mview_dbuf[5], &mview_dbuf[6], &mview_dbuf[7]) < 4)) {
1600 s_append_asprintf(errbuf, "line %d: bad trackball format", lineNumber);
1606 } else if (strcmp(buf, "!:view") == 0) {
1607 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1613 comp = strsep(&bufp, " \t");
1615 while (*bufp == ' ' || *bufp == '\t')
1617 valp = strsep(&bufp, "\n");
1619 /* In the following, the redundant "!= NULL" is to suppress suprious warning */
1620 if ((strcmp(comp, "show_unit_cell") == 0 && (i = 1))
1621 || (strcmp(comp, "show_periodic_box") == 0 && (i = 2))
1622 || (strcmp(comp, "show_expanded_atoms") == 0 && (i = 3))
1623 || (strcmp(comp, "show_ellipsoids") == 0 && (i = 4))
1624 || (strcmp(comp, "show_hydrogens") == 0 && (i = 5))
1625 || (strcmp(comp, "show_dummy_atoms") == 0 && (i = 6))
1626 || (strcmp(comp, "show_rotation_center") == 0 && (i = 7))
1627 || (strcmp(comp, "show_graphite_flag") == 0 && (i = 8))
1628 || (strcmp(comp, "show_periodic_image_flag") == 0 && (i = 9))
1629 || (strcmp(comp, "show_graphite") == 0 && (i = 10))
1630 || (strcmp(comp, "atom_resolution") == 0 && (i = 11))
1631 || (strcmp(comp, "bond_resolution") == 0 && (i = 12))) {
1632 mview_ibuf[i - 1] = atoi(valp);
1633 } else if ((strcmp(comp, "atom_radius") == 0 && (i = 8))
1634 || (strcmp(comp, "bond_radius") == 0 && (i = 9))) {
1635 mview_dbuf[i] = strtod(valp, NULL);
1636 } else if (strcmp(comp, "show_periodic_image") == 0) {
1637 sscanf(valp, "%d %d %d %d %d %d",
1638 &mview_ibuf[12], &mview_ibuf[13], &mview_ibuf[14],
1639 &mview_ibuf[15], &mview_ibuf[16], &mview_ibuf[17]);
1644 /* Unknown sections are silently ignored */
1647 MoleculeCleanUpResidueTable(mp);
1648 if (mp->arena != NULL)
1649 md_arena_set_molecule(mp->arena, mp);
1652 if (mp->mview != NULL) {
1653 if (mview_ibuf[0] != kUndefined)
1654 mp->mview->showUnitCell = mview_ibuf[0];
1655 if (mview_ibuf[1] != kUndefined)
1656 mp->mview->showPeriodicBox = mview_ibuf[1];
1657 if (mview_ibuf[2] != kUndefined)
1658 mp->mview->showExpandedAtoms = mview_ibuf[2];
1659 if (mview_ibuf[3] != kUndefined)
1660 mp->mview->showEllipsoids = mview_ibuf[3];
1661 if (mview_ibuf[4] != kUndefined)
1662 mp->mview->showHydrogens = mview_ibuf[4];
1663 if (mview_ibuf[5] != kUndefined)
1664 mp->mview->showDummyAtoms = mview_ibuf[5];
1665 if (mview_ibuf[6] != kUndefined)
1666 mp->mview->showRotationCenter = mview_ibuf[6];
1667 if (mview_ibuf[7] != kUndefined)
1668 mp->mview->showGraphiteFlag = mview_ibuf[7];
1669 if (mview_ibuf[8] != kUndefined)
1670 mp->mview->showPeriodicImageFlag = mview_ibuf[8];
1671 if (mview_ibuf[9] != kUndefined)
1672 mp->mview->showGraphite = mview_ibuf[9];
1673 if (mview_ibuf[10] != kUndefined && mview_ibuf[10] >= 6)
1674 mp->mview->atomResolution = mview_ibuf[10];
1675 if (mview_ibuf[11] != kUndefined && mview_ibuf[11] >= 4)
1676 mp->mview->bondResolution = mview_ibuf[11];
1677 for (i = 0; i < 6; i++) {
1678 if (mview_ibuf[12 + i] != kUndefined)
1679 mp->mview->showPeriodicImage[i] = mview_ibuf[12 + i];
1681 if (mview_dbuf[8] != kUndefined)
1682 mp->mview->atomRadius = mview_dbuf[8];
1683 if (mview_dbuf[9] != kUndefined)
1684 mp->mview->bondRadius = mview_dbuf[9];
1685 if (mp->mview->track != NULL) {
1686 if (mview_dbuf[0] != kUndefined)
1687 TrackballSetScale(mp->mview->track, mview_dbuf[0]);
1688 if (mview_dbuf[1] != kUndefined)
1689 TrackballSetTranslate(mp->mview->track, mview_dbuf + 1);
1690 if (mview_dbuf[4] != kUndefined)
1691 TrackballSetRotate(mp->mview->track, mview_dbuf + 4);
1699 /* The content of mp may be broken, so make it empty */
1705 MoleculeLoadPsfFile(Molecule *mp, const char *fname, char **errbuf)
1714 Vector *frames = NULL;
1720 else MoleculeClear(mp);
1721 fp = fopen(fname, "rb");
1723 s_append_asprintf(errbuf, "Cannot open file");
1726 /* flockfile(fp); */
1729 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1730 if (strncmp(buf, "PSF", 3) == 0) {
1734 for (p = buf; *p != 0 && isspace(*p); p++) {}
1740 if (strstr(buf, "!COORD") != NULL) {
1741 /* Extended psf file with coordinates */
1743 /* Allocate a temporary storage for frames */
1744 size_t size = sizeof(Vector) * mp->natoms * fn;
1746 frames = (Vector *)malloc(size);
1748 frames = (Vector *)realloc(frames, size);
1752 /* Read coordinates */
1753 for (i = 0; i < mp->natoms; i++) {
1756 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1758 s_append_asprintf(errbuf, "line %d: premature end of file while reading coordinates (frame %d)", lineNumber, fn);
1761 if (sscanf(buf, "%lg %lg %lg", dval, dval + 1, dval + 2) != 3) {
1763 s_append_asprintf(errbuf, "line %d: coordinates cannot be read for atom %d", lineNumber, i + 1);
1770 ATOM_AT_INDEX(mp->atoms, i)->r = r;
1772 frames[mp->natoms * (fn - 1) + i] = r;
1781 ReadFormat(buf, "I8", &natoms);
1784 if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
1787 for (i = 0; i < natoms; i++) {
1789 char segName[5], resName[4], atomName[5], atomType[3], element[3];
1792 memset(&w, 0, sizeof(w));
1793 ap = ATOM_AT_INDEX(mp->atoms, i);
1794 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1796 s_append_asprintf(errbuf, "line %d: premature end of file while reading atoms", lineNumber);
1799 ReadFormat(buf, "I8 x1 S4 I5 x1 S3 x2 S4 x1 S4 F16 F10",
1800 &w.serial, w.segName, &ap->resSeq, w.resName, w.atomName,
1801 w.atomType, &ap->charge, &ap->weight);
1802 strncpy(ap->segName, w.segName, 4);
1803 strncpy(ap->resName, w.resName, 3);
1804 strncpy(ap->aname, w.atomName, 4);
1805 ap->type = AtomTypeEncodeToUInt(w.atomType);
1806 /* $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl */
1807 ap->atomicNumber = GuessAtomicNumber(w.atomName, ap->weight);
1808 ElementToString(ap->atomicNumber, w.element);
1809 strncpy(ap->element, w.element, 2);
1810 /* w.element[0] = 0;
1811 for (p = w.atomName; *p != 0; p++) {
1812 if (isalpha(*p) && *p != '_') {
1813 w.element[0] = toupper(*p);
1814 if (isalpha(p[1]) && p[1] != '_') {
1815 w.element[1] = toupper(p[1]);
1823 strncpy(ap->element, w.element, 2);
1824 ap->atomicNumber = ElementToInt(w.element); */
1825 if (w.resName[0] == 0)
1826 strncpy(ap->resName, "XXX", 3);
1827 if (ap->resSeq > mp->nresidues)
1828 mp->nresidues = ap->resSeq;
1830 if (mp->residues != NULL)
1832 if (NewArray(&mp->residues, &mp->nresidues, sizeof(char (*)[4]), mp->nresidues + 1) == 0)
1834 for (i = 0; i < mp->natoms; i++) {
1835 j = mp->atoms[i].resSeq;
1836 if (mp->residues[j][0] == 0)
1837 strncpy(mp->residues[j], mp->atoms[i].resName, 4);
1840 } else if (section == 3) {
1844 ReadFormat(buf, "I8", &nbonds);
1847 if (NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, nbonds) == NULL)
1850 for (i = 0; i < nbonds; i += 4) {
1851 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1852 s_append_asprintf(errbuf, "line %d: premature end of file while reading bonds", lineNumber);
1856 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1857 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1858 for (j = 0; j < 4 && i + j < nbonds; j++) {
1861 b1 = ibuf[j * 2] - 1; /* Internal atom number is 0-based */
1862 b2 = ibuf[j * 2 + 1] - 1;
1863 if (b1 < 0 || b1 >= mp->natoms || b2 < 0 || b2 >= mp->natoms) {
1864 s_append_asprintf(errbuf, "line %d: The bond %d-%d includes non-existent atom", lineNumber, b1+1, b2+1);
1870 ap = ATOM_AT_INDEX(mp->atoms, b1);
1871 AtomConnectInsertEntry(&ap->connect, -1, b2);
1872 ap = ATOM_AT_INDEX(mp->atoms, b2);
1873 AtomConnectInsertEntry(&ap->connect, -1, b1);
1877 } else if (section == 4) {
1881 ReadFormat(buf, "I8", &nangles);
1884 if (NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, nangles) == NULL)
1887 for (i = 0; i < nangles; i += 3) {
1888 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1889 s_append_asprintf(errbuf, "line %d: premature end of file while reading angles", lineNumber);
1893 ReadFormat(buf, "I8I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1894 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7, ibuf + 8);
1895 for (j = 0; j < 3 && i + j < nangles; j++) {
1897 a1 = ibuf[j * 3] - 1; /* Internal atom number is 0-based */
1898 a2 = ibuf[j * 3 + 1] - 1;
1899 a3 = ibuf[j * 3 + 2] - 1;
1900 if (a1 < 0 || a1 >= mp->natoms || a2 < 0 || a2 >= mp->natoms || a3 < 0 || a3 >= mp->natoms) {
1901 s_append_asprintf(errbuf, "line %d: The angle %d-%d-%d includes non-existent atom", lineNumber, a1+1, a2+1, a3+1);
1911 } else if (section == 5 || section == 6) {
1912 /* Dihedrals and Impropers */
1915 ReadFormat(buf, "I8", &ndihedrals);
1916 if (ndihedrals == 0)
1919 if (NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, ndihedrals) == NULL)
1923 if (NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, ndihedrals) == NULL)
1927 for (i = 0; i < ndihedrals; i += 2) {
1928 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1930 s_append_asprintf(errbuf, "line %d: premature end of file while reading %s", lineNumber, (section == 5 ? "dihedral" : "improper"));
1934 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3, ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1935 for (j = 0; j < 2 && i + j < ndihedrals; j++) {
1937 d1 = ibuf[j * 4] - 1; /* Internal atom number is 0-based */
1938 d2 = ibuf[j * 4 + 1] - 1;
1939 d3 = ibuf[j * 4 + 2] - 1;
1940 d4 = ibuf[j * 4 + 3] - 1;
1941 if (d1 < 0 || d1 >= mp->natoms || d2 < 0 || d2 >= mp->natoms || d3 < 0 || d3 >= mp->natoms || d4 < 0 || d4 >= mp->natoms) {
1942 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);
1956 /* Create frames for each atom if necessary */
1958 for (i = 0; i < mp->natoms; i++) {
1959 ap = ATOM_AT_INDEX(mp->atoms, i);
1960 ap->frames = (Vector *)malloc(sizeof(Vector) * fn);
1961 if (ap->frames == NULL)
1964 for (j = 0; j < fn; j++)
1965 ap->frames[j] = frames[mp->natoms * j + i];
1972 /* funlockfile(fp); */
1974 mp->nframes = -1; /* Should be recalculated later */
1977 else if (section == -1)
1981 Panic("low memory while reading structure file %s", fname);
1982 return 1; /* not reached */
1985 /* ("-x", "y", "-z+0.5") -> (-1,0,0,0,0,1,0,0,0,0,-1,0.5) */
1987 sMoleculeSymopStringsToTransform(char **symops, Transform tr)
1991 memset(tr, 0, sizeof(Transform));
1992 for (i = 0; i < 3; i++) {
1996 while (*symop != 0) {
1998 while (isspace(*symop))
2000 if (*symop == 0 || *symop == '\r' || *symop == 'n')
2002 if (*symop == '-') {
2005 } else if (*symop == '+') {
2009 while (isspace(*symop))
2011 if (*symop == '.' || isdigit(*symop)) {
2012 /* Numerical offset */
2013 double d = strtod(symop, &symop);
2014 if (*symop == '/') {
2015 double dd = strtod(symop + 1, &symop);
2019 return 1; /* Bad format */
2022 } else if (*symop == 'x' || *symop == 'X') {
2025 } else if (*symop == 'y' || *symop == 'Y') {
2028 } else if (*symop == 'z' || *symop == 'Z') {
2031 } else return 1; /* Bad format */
2032 } /* end while (*symop != 0) */
2038 sMoleculeGenerateSymopWithTransform(Molecule *mp, Transform gtr, int num)
2044 for (i = 0; i < num; i++) {
2045 memmove(tr, mp->syms[i], sizeof(Transform));
2046 TransformMul(tr, gtr, tr);
2047 for (j = 9; j < 12; j++) {
2050 else if (tr[j] <= 0.0)
2053 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
2060 char *p = buf + strlen(buf) - 1;
2061 if (p >= buf && (*p == '\n' || *p == '\r')) {
2063 if (--p >= buf && (*p == '\n' || *p == '\r')) {
2071 MoleculeLoadTepFile(Molecule *mp, const char *fname, char **errbuf)
2084 fp = fopen(fname, "rb");
2086 s_append_asprintf(errbuf, "Cannot open file");
2090 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2091 if (section == -1) {
2098 ReadFormat(buf, "I1F8F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5);
2100 MoleculeSetCell(mp, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], 0);
2107 if (cellType == 0) {
2108 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);
2122 char *symops[3], *brks;
2124 memset(tr, 0, sizeof(Transform));
2125 ReadFormat(buf, "I1", ibuf);
2126 symops[0] = strtok_r(buf + 1, ", ", &brks);
2127 symops[1] = strtok_r(NULL, ", ", &brks);
2128 symops[2] = strtok_r(NULL, ", ", &brks);
2129 if (sMoleculeSymopStringsToTransform(symops, tr)) {
2130 s_append_asprintf(errbuf, "line %d: bad symmetry specification", lineNumber);
2134 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2140 if (section == 2) { /* Atoms */
2144 int atomIndex = mp->natoms;
2145 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2146 memset(ap, 0, gSizeOfAtomRecord);
2147 ReadFormat(buf, "S6x3x9x9F9F9F9F9", name, fbuf, fbuf+1, fbuf+2, fbuf+3);
2148 strncpy(ap->aname, name, 4);
2152 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2153 /* ap->atomicNumber = AtomNameToElement(ap->name);
2154 ElementToString(ap->atomicNumber, ap->element); */
2155 /* sAtomSetElement(ap, -1, ap->name); */
2158 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2159 s_append_asprintf(errbuf, "unexpected end of file");
2162 ReadFormat(buf, "I1F8F9F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2164 if ((atomType >= 0 && atomType <= 5) || (atomType >= 8 && atomType <= 10)) {
2165 /* Anisotropic thermal parameters */
2166 MoleculeSetAniso(mp, atomIndex, atomType, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[5], fbuf[4], NULL);
2174 MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
2176 MoleculeAddBonds(mp, nbonds, bonds, NULL, 1);
2179 mp->nframes = -1; /* Should be recalculated later */
2182 Panic("low memory while reading structure file %s", fname);
2183 return -1; /* not reached */
2187 MoleculeLoadShelxFile(Molecule *mp, const char *fname, char **errbuf)
2195 int currentResSeq = 0;
2196 char currentResName[6];
2203 char (*sfacs)[4] = NULL;
2208 currentResName[0] = 0;
2209 fp = fopen(fname, "rb");
2211 s_append_asprintf(errbuf, "Cannot open file");
2215 tr[0] = tr[4] = tr[8] = 1;
2216 tr[1] = tr[2] = tr[3] = tr[5] = tr[6] = tr[7] = tr[9] = tr[10] = tr[11] = 0;
2217 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), 0, tr) == 0)
2219 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2220 if (strncmp(buf, "CELL", 4) == 0) {
2222 sscanf(buf + 4, " %f %f %f %f %f %f %f", fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2223 MoleculeSetCell(mp, fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], fbuf[6], 0);
2225 } else if (strncmp(buf, "SFAC", 4) == 0) {
2227 for (p1 = strtok_r(buf + 4, " ", &p2); p1 != NULL; p1 = strtok_r(NULL, " ", &p2)) {
2228 char *pp = (char *)AssignArray(&sfacs, &nsfacs, 4, nsfacs, NULL);
2235 } else if (strncmp(buf, "LATT", 4) == 0) {
2236 sscanf(buf + 4, " %d", &latticeType);
2238 } else if (strncmp(buf, "SYMM", 4) == 0) {
2239 char *symops[3], *brks;
2240 memset(tr, 0, sizeof(Transform));
2241 // ReadFormat(buf + 4, "I1", ibuf);
2243 symops[0] = strtok_r(buf + 4, ",", &brks);
2244 symops[1] = strtok_r(NULL, ",", &brks);
2245 symops[2] = strtok_r(NULL, ",", &brks);
2246 if (sMoleculeSymopStringsToTransform(symops, tr)) {
2247 s_append_asprintf(errbuf, "line %d: bad symmetry specification", lineNumber);
2250 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2253 } else if (strncmp(buf, "RESI", 4) == 0) {
2254 for (p1 = buf + 4; isspace(*p1); p1++);
2256 for (p2 = p1 + 1; isalnum(*p2); p2++);
2258 strncpy(currentResName, p1, 4);
2259 currentResName[4] = 0;
2261 } else currentResName[0] = 0;
2262 sscanf(buf + 4, " %d", ¤tResSeq);
2265 /* Atom name: [A-Za-z]{1,2}[0-9]* */
2266 for (p1 = buf; p1 < buf + 2 && (isalpha(*p1) && *p1 != '_'); p1++);
2268 while (isdigit(*p1))
2271 if (p1 > buf && p1 <= buf + 4 && isspace(*p1)) {
2275 int atomIndex = mp->natoms;
2276 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2277 memset(ap, 0, gSizeOfAtomRecord);
2278 strncpy(ap->aname, buf, 4);
2279 n = sscanf(p1, " %d %f %f %f %f %f %f %2s", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, cont);
2280 if (n == 8 && strcmp(cont, "=") == 0) {
2281 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2282 s_append_asprintf(errbuf, "line %d: unexpected end of file within the atom cards", lineNumber);
2285 sscanf(buf, " %f %f %f %f", fbuf+6, fbuf+7, fbuf+8, fbuf+9);
2287 } else n = 5; /* Iso */
2291 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2292 ap->occupancy = fbuf[3];
2293 if (ap->aname[0] != 'Q' && ibuf[0] >= 1 && ibuf[0] <= nsfacs) {
2294 strncpy(ap->element, sfacs[ibuf[0] - 1], 2);
2296 /* sAtomSetElement(ap, -1, sfacs[ibuf[0] - 1]); */
2297 /* strncpy(ap->element, sfacs[ibuf[0] - 1], 4);
2298 ap->atomicNumber = ElementToInt(ap->element); */
2300 sAtomSetElement(ap, -1, ap->name); */
2301 /* ap->atomicNumber = AtomNameToElement(ap->name);
2302 ElementToString(ap->atomicNumber, ap->element); */
2305 if (n == 10 || fbuf[4] >= 0.0) {
2307 /* Read in the standard deviations */
2308 ReadLine(buf, sizeof buf, fp, &lineNumber);
2309 for (i = 0; i < 9; i++) {
2313 dbuf[i] = strtod(buf + j, NULL);
2316 ap->sigma.x = dbuf[0];
2317 ap->sigma.y = dbuf[1];
2318 ap->sigma.z = dbuf[2];
2321 ap->tempFactor = fbuf[4] * 78.9568352087147; /* 8*pi*pi */
2323 MoleculeSetAniso(mp, atomIndex, 8, fbuf[4], fbuf[5], fbuf[6], fbuf[9], fbuf[7], fbuf[8], dbuf);
2324 ap->resSeq = currentResSeq;
2325 strncpy(ap->resName, currentResName, 4);
2332 /* Add symmetry operations according to the lattice type */
2333 switch (latticeType < 0 ? -latticeType : latticeType) {
2334 static Transform tr_i = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0.5};
2335 static Transform tr_c = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0};
2336 static Transform tr_a = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.5};
2337 static Transform tr_b = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.5};
2338 static Transform tr_r1 = {0, 1, 0, -1, -1, 0, 0, 0, 1, 0, 0, 0};
2339 static Transform tr_r2 = {-1, -1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0};
2343 sMoleculeGenerateSymopWithTransform(mp, tr_i, 0);
2345 case 3: /* Rhombohedral obverse on hexagonal axes */
2347 sMoleculeGenerateSymopWithTransform(mp, tr_r1, n);
2348 sMoleculeGenerateSymopWithTransform(mp, tr_r2, n);
2352 sMoleculeGenerateSymopWithTransform(mp, tr_a, n);
2353 sMoleculeGenerateSymopWithTransform(mp, tr_b, n);
2354 sMoleculeGenerateSymopWithTransform(mp, tr_c, n);
2357 sMoleculeGenerateSymopWithTransform(mp, tr_a, 0);
2360 sMoleculeGenerateSymopWithTransform(mp, tr_b, 0);
2363 sMoleculeGenerateSymopWithTransform(mp, tr_c, 0);
2367 if (latticeType > 0) {
2368 static Transform tr_inv = {-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0};
2369 sMoleculeGenerateSymopWithTransform(mp, tr_inv, 0);
2372 MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
2374 MoleculeAddBonds(mp, nbonds, bonds, NULL, 1);
2377 mp->nframes = -1; /* Should be recalculated later */
2380 Panic("low memory while reading structure file %s", fname);
2381 return -1; /* not reached */
2384 /* Add one gaussian orbital shell information (not undoable) */
2386 MoleculeAddGaussianOrbitalShell(Molecule *mol, Int sym, Int nprims, Int a_idx)
2391 return -1; /* Molecule is empty */
2394 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2396 return -2; /* Low memory */
2398 shellp = AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), bset->nshells, NULL);
2400 return -2; /* Low memory */
2402 case 0: shellp->sym = kGTOType_S; shellp->ncomp = 1; break;
2403 case 1: shellp->sym = kGTOType_P; shellp->ncomp = 3; break;
2404 case -1: shellp->sym = kGTOType_SP; shellp->ncomp = 4; break;
2405 case 2: shellp->sym = kGTOType_D; shellp->ncomp = 6; break;
2406 case -2: shellp->sym = kGTOType_D5; shellp->ncomp = 5; break;
2407 case 3: shellp->sym = kGTOType_F; shellp->ncomp = 10; break;
2408 case -3: shellp->sym = kGTOType_F7; shellp->ncomp = 7; break;
2409 case 4: shellp->sym = kGTOType_G; shellp->ncomp = 15; break;
2410 case -4: shellp->sym = kGTOType_G9; shellp->ncomp = 9; break;
2412 return -3; /* Unsupported shell type */
2414 shellp->nprim = nprims;
2415 shellp->a_idx = a_idx;
2416 if (bset->shells < shellp) {
2417 shellp->m_idx = shellp[-1].m_idx + shellp[-1].ncomp;
2418 shellp->p_idx = shellp[-1].p_idx + shellp[-1].nprim;
2426 /* Add a set of gaussian primitive coefficients (not undoable) */
2428 MoleculeAddGaussianPrimitiveCoefficients(Molecule *mol, Double exponent, Double contraction, Double contraction_sp)
2433 return -1; /* Molecule is empty */
2436 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2438 return -2; /* Low memory */
2440 primp = AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), bset->npriminfos, NULL);
2442 return -2; /* Low memory */
2443 primp->A = exponent;
2444 primp->C = contraction;
2445 primp->Csp = contraction_sp;
2449 /* Set MO coefficients for idx-th MO */
2451 MoleculeSetMOCoefficients(Molecule *mol, Int idx, Double energy, Int ncomps, Double *coeffs)
2456 return -1; /* Molecule is empty */
2459 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2461 return -2; /* Low memory */
2463 if (bset->nmos == 0) {
2464 if (bset->nshells > 0) {
2465 /* Shell info is already set: calculate the number of MOs from there */
2466 for (i = n = 0; i < bset->nshells; i++)
2467 n += bset->shells[i].ncomp;
2469 } else if (ncomps > 0) {
2470 bset->ncomps = ncomps;
2472 if (bset->rflag == 0)
2473 bset->nmos = bset->ncomps * 2;
2475 bset->nmos = bset->ncomps;
2476 if (bset->nmos <= 0)
2477 return -3; /* Bad or inconsistent number of MOs */
2478 bset->mo = (Double *)calloc(sizeof(Double), bset->nmos * bset->ncomps);
2479 bset->moenergies = (Double *)calloc(sizeof(Double), bset->nmos);
2480 if (bset->mo == NULL || bset->moenergies == NULL) {
2481 if (bset->mo != NULL)
2483 if (bset->moenergies != NULL)
2484 free(bset->moenergies);
2486 bset->moenergies = NULL;
2488 return -2; /* Low memory */
2491 if (idx < 0 || idx >= bset->nmos)
2492 return -4; /* Bad MO index */
2493 if (energy != -1000000)
2494 bset->moenergies[idx] = energy;
2495 if (ncomps < bset->ncomps)
2496 return -5; /* Insufficient number of data provided */
2497 memmove(bset->mo + (idx * bset->ncomps), coeffs, sizeof(Double) * bset->ncomps);
2498 if (bset->cns != NULL) {
2499 /* Clear the cached values */
2507 /* Get MO coefficients for idx-th MO */
2508 /* Caution: *ncoeffs and *coeffs should be valid _before_ calling this function, i.e. */
2509 /* *ncoeffs = 0 && *coeffs = NULL or *coeffs is a valid memory pointer and *ncoeffs */
2510 /* properly designates the memory size as an array of Doubles. */
2512 MoleculeGetMOCoefficients(Molecule *mol, Int idx, Double *energy, Int *ncoeffs, Double **coeffs)
2516 return -1; /* Molecule is empty */
2518 if (bset == NULL || bset->ncomps <= 0)
2519 return -2; /* No basis set info */
2520 if (idx < 0 || idx >= bset->nmos)
2521 return -3; /* MO index out of range */
2523 *energy = bset->moenergies[idx];
2524 if (ncoeffs != NULL && coeffs != NULL) {
2525 if (*ncoeffs < bset->ncomps || *coeffs == NULL) {
2526 if (*coeffs != NULL)
2527 free(*coeffs); /* Caution: possible cause of SIGBUS if *coeff is not initialized properly */
2528 *coeffs = (Double *)calloc(sizeof(Double), bset->ncomps);
2529 *ncoeffs = bset->ncomps;
2531 memmove(*coeffs, bset->mo + (idx * bset->ncomps), sizeof(Double) * bset->ncomps);
2536 /* Allocate BasisSet record. rflag: UHF, 0; RHF, 1; ROHF, 2
2537 ne_alpha: number of alpha electrons, ne_beta: number of beta electrons
2538 The natoms and pos are copied from mol. */
2540 MoleculeAllocateBasisSetRecord(Molecule *mol, Int rflag, Int ne_alpha, Int ne_beta)
2545 if (mol == NULL || mol->natoms == 0)
2546 return -1; /* Molecule is empty */
2549 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2551 return -2; /* Low memory */
2553 bset->natoms_bs = mol->natoms;
2554 bset->ne_alpha = ne_alpha;
2555 bset->ne_beta = ne_beta;
2556 bset->rflag = rflag;
2561 sSeparateTokens(char *inString, char **outPtr, int size)
2565 for (i = 0; i < size; i++) {
2566 p = strtok((i == 0 ? inString : NULL), " \r\n");
2577 sReadNumberArray(void *basep, Int *countp, Int size, Int num, FILE *fp, int *lnp)
2581 *((void **)basep) = NULL;
2583 if (AssignArray(basep, countp, size, num - 1, NULL) == NULL)
2584 return 4; /* Out of memory */
2586 while (ReadLine(buf, sizeof buf, fp, lnp) > 0) {
2587 char *tokens[16], *p;
2588 sSeparateTokens(buf, tokens, 16);
2589 for (i = 0; i < 16; i++) {
2590 if (tokens[i] == NULL)
2592 if (size == sizeof(Int)) {
2593 (*((Int **)basep))[n] = strtol(tokens[i], &p, 0);
2594 } else if (size == sizeof(Double)) {
2595 (*((Double **)basep))[n] = strtod(tokens[i], &p);
2596 } else return -1; /* Internal error */
2597 if (tokens[i] == p || *p != 0)
2598 return 1; /* Non-digit character */
2600 if (i < 15 && tokens[i + 1] != NULL)
2601 return 2; /* Too many data */
2602 return 0; /* All data are successfully read */
2606 return 3; /* Unexpected EOF */
2610 sSetupGaussianCoefficients(BasisSet *bset)
2617 /* Cache the contraction coefficients for efficient calculation */
2618 /* Sum up the number of components for all primitives */
2619 for (i = k = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2621 k += sp->nprim * sp->ncomp;
2623 /* Allocate memory for the cached values */
2624 if (AssignArray(&bset->cns, &bset->ncns, sizeof(Double), k - 1, NULL) == NULL)
2626 /* Iterate over all primitives */
2628 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2629 for (j = 0, pp = bset->priminfos + sp->p_idx; j < sp->nprim; j++, pp++) {
2632 // (8 alpha^3/pi^3)^0.25 exp(-alpha r^2)
2633 *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2636 // (128 alpha^5/pi^3)^0.25 [x|y|z]exp(-alpha r^2)
2637 d = pp->C * pow(pp->A, 1.25) * 1.425410941;
2643 *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2644 d = pp->Csp * pow(pp->A, 1.25) * 1.425410941;
2650 // xx|yy|zz: (2048 alpha^7/9pi^3)^0.25 [xx|yy|zz]exp(-alpha r^2)
2651 // xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2652 d = pp->C * pow(pp->A, 1.75);
2653 dp[0] = dp[1] = dp[2] = d * 1.645922781;
2654 dp[3] = dp[4] = dp[5] = d * 2.850821881;
2658 // 3zz-rr: (128 alpha^7/9pi^3)^0.25 (3zz-rr)exp(-alpha r^2)
2659 // xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2660 // xx-yy: (128 alpha^7/pi^3)^0.25 (xx-yy)exp(-alpha r^2)
2661 d = pp->C * pow(pp->A, 1.75);
2662 dp[0] = d * 0.822961390;
2663 dp[1] = dp[2] = dp[4] = d * 2.850821881;
2664 dp[3] = d * 1.425410941;
2667 /* TODO: Support F/F7 and G/G9 type orbitals */
2675 MoleculeLoadGaussianFchkFile(Molecule *mp, const char *fname, char **errbuf)
2680 int natoms, nbasis, i, j, k, n, mxbond, retval, ncomps, nprims, nelec;
2694 bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2698 fp = fopen(fname, "rb");
2700 s_append_asprintf(errbuf, "Cannot open file");
2704 natoms = nbasis = -1;
2712 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2715 if (lineNumber == 2) {
2718 bset->rflag = 0; /* UHF */
2719 else if (buf[11] == 'O')
2720 bset->rflag = 2; /* ROHF */
2721 else bset->rflag = 1; /* RHF */
2724 while (p > buf && *p == ' ')
2727 sSeparateTokens(buf + 42, tokens, 16);
2728 if (strcmp(buf, "Number of atoms") == 0) {
2729 if (tokens[1] == NULL || (natoms = atoi(tokens[1])) <= 0) {
2730 s_append_asprintf(errbuf, "Line %d: strange number of atoms: %s", lineNumber, tokens[1]);
2734 bset->natoms_bs = natoms;
2735 /* Allocate atom records (all are empty for now) */
2736 AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms - 1, NULL);
2737 /* Also allocate atom position array for MO calculations */
2738 /* AssignArray(&bset->pos, &bset->natoms, sizeof(Vector), natoms - 1, NULL); */
2739 /* Also allocate nuclear charge array */
2740 bset->nuccharges = (Double *)calloc(sizeof(Double), natoms);
2741 } else if (strcmp(buf, "Number of electrons") == 0) {
2742 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2743 s_append_asprintf(errbuf, "Line %d: strange number of electrons: %s", lineNumber, tokens[1]);
2748 } else if (strcmp(buf, "Number of alpha electrons") == 0) {
2749 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2750 s_append_asprintf(errbuf, "Line %d: strange number of alpha electrons: %s", lineNumber, tokens[1]);
2755 } else if (strcmp(buf, "Number of beta electrons") == 0) {
2756 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2757 s_append_asprintf(errbuf, "Line %d: strange number of beta electrons: %s", lineNumber, tokens[1]);
2762 if (bset->ne_alpha + bset->ne_beta != nelec) {
2763 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);
2767 } else if (strcmp(buf, "Number of basis functions") == 0) {
2768 if (tokens[1] == NULL || (nbasis = atoi(tokens[1])) <= 0) {
2769 s_append_asprintf(errbuf, "Line %d: strange number of basis functions: %s", lineNumber, tokens[1]);
2773 } else if (strcmp(buf, "Atomic numbers") == 0) {
2774 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2775 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2779 if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms, fp, &lineNumber) != 0) {
2780 s_append_asprintf(errbuf, "Line %d: cannot read atomic numbers", lineNumber);
2784 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2785 ap->atomicNumber = iary[i];
2786 bset->nuccharges[i] = iary[i];
2787 ElementToString(ap->atomicNumber, ap->element);
2788 memmove(ap->aname, ap->element, 4);
2789 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
2794 } else if (strcmp(buf, "Nuclear charges") == 0) {
2795 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2796 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2800 if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms, fp, &lineNumber) != 0) {
2801 s_append_asprintf(errbuf, "Line %d: cannot read nuclear charges", lineNumber);
2805 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2806 bset->nuccharges[i] = dary[i];
2810 } else if (strcmp(buf, "Current cartesian coordinates") == 0) {
2811 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * 3) {
2812 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of cartesian coordinates: %s", lineNumber, tokens[2]);
2816 if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms * 3, fp, &lineNumber) != 0) {
2817 s_append_asprintf(errbuf, "Line %d: cannot read cartesian coordinates", lineNumber);
2821 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2822 ap->r.x = dary[i * 3] * kBohr2Angstrom;
2823 ap->r.y = dary[i * 3 + 1] * kBohr2Angstrom;
2824 ap->r.z = dary[i * 3 + 2] * kBohr2Angstrom;
2828 } else if (strcmp(buf, "MxBond") == 0) {
2829 if (tokens[1] == NULL || (mxbond = atoi(tokens[1])) <= 0) {
2830 s_append_asprintf(errbuf, "Line %d: strange number of bonds per atom: %s", lineNumber, tokens[1]);
2834 } else if (strcmp(buf, "IBond") == 0) {
2836 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * mxbond) {
2837 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of bonds: %s", lineNumber, tokens[2]);
2841 if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms * mxbond, fp, &lineNumber) != 0) {
2842 s_append_asprintf(errbuf, "Line %d: cannot read bond information", lineNumber);
2846 bonds = (Int *)malloc(sizeof(Int) * (mxbond * 2 + 1));
2847 for (i = 0; i < natoms; i++) {
2848 for (j = k = 0; j < mxbond; j++) {
2849 n = iary[i * mxbond + j] - 1;
2851 /* Connect atom i and atom n */
2857 bonds[k] = kInvalidIndex;
2858 MoleculeAddBonds(mp, k / 2, bonds, NULL, 1);
2864 } else if (strcmp(buf, "Shell types") == 0) {
2865 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0) {
2866 s_append_asprintf(errbuf, "Line %d: wrong number of shell types: %s", lineNumber, tokens[2]);
2870 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2871 s_append_asprintf(errbuf, "Line %d: cannot read shell types", lineNumber);
2875 /* Allocate ShellInfo table and store shell type information */
2876 AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), nary - 1, NULL);
2877 for (i = n = 0, sp = bset->shells; i < nary; i++, sp++) {
2879 case 0: sp->sym = kGTOType_S; sp->ncomp = 1; break;
2880 case 1: sp->sym = kGTOType_P; sp->ncomp = 3; break;
2881 case -1: sp->sym = kGTOType_SP; sp->ncomp = 4; break;
2882 case 2: sp->sym = kGTOType_D; sp->ncomp = 6; break;
2883 case -2: sp->sym = kGTOType_D5; sp->ncomp = 5; break;
2884 case 3: sp->sym = kGTOType_F; sp->ncomp = 10; break;
2885 case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break;
2886 case 4: sp->sym = kGTOType_G; sp->ncomp = 15; break;
2887 case -4: sp->sym = kGTOType_G9; sp->ncomp = 9; break;
2889 s_append_asprintf(errbuf, "Line %d: unsupported shell type %d", lineNumber, iary[i]);
2896 bset->ncomps = ncomps = n;
2899 } else if (strcmp(buf, "Number of primitives per shell") == 0) {
2900 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2901 s_append_asprintf(errbuf, "Line %d: wrong size of the primitive table: %s", lineNumber, tokens[2]);
2905 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2906 s_append_asprintf(errbuf, "Line %d: cannot read primitive table", lineNumber);
2910 for (i = n = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2911 sp->nprim = iary[i];
2918 } else if (strcmp(buf, "Shell to atom map") == 0) {
2919 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2920 s_append_asprintf(errbuf, "Line %d: wrong size of the shell-to-atom map: %s", lineNumber, tokens[2]);
2924 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2925 s_append_asprintf(errbuf, "Line %d: cannot read shell-to-atom table", lineNumber);
2929 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2930 sp->a_idx = iary[i] - 1;
2934 } else if (strcmp(buf, "Primitive exponents") == 0) {
2935 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != nprims) {
2936 s_append_asprintf(errbuf, "Line %d: wrong number of primitive exponents: %s", lineNumber, tokens[2]);
2940 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2941 s_append_asprintf(errbuf, "Line %d: cannot read primitive exponents", lineNumber);
2945 /* Allocate PrimInfo table */
2946 AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), nprims - 1, NULL);
2947 for (i = 0, pp = bset->priminfos; i < nprims; i++, pp++) {
2952 } else if (strcmp(buf, "Contraction coefficients") == 0) {
2953 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2954 s_append_asprintf(errbuf, "Line %d: wrong number of contraction coefficients: %s", lineNumber, tokens[2]);
2958 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2959 s_append_asprintf(errbuf, "Line %d: cannot read contraction coefficients", lineNumber);
2963 for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2968 } else if (strcmp(buf, "P(S=P) Contraction coefficients") == 0) {
2969 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2970 s_append_asprintf(errbuf, "Line %d: wrong number of P(S=P) contraction coefficients: %s", lineNumber, tokens[2]);
2974 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2975 s_append_asprintf(errbuf, "Line %d: cannot read P(S=P) contraction coefficients", lineNumber);
2979 for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2984 } else if (strcmp(buf, "Alpha Orbital Energies") == 0) {
2985 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2986 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha orbitals: %s", lineNumber, tokens[2]);
2990 if (sReadNumberArray(&bset->moenergies, &bset->nmos, sizeof(Double), i, fp, &lineNumber) != 0) {
2991 s_append_asprintf(errbuf, "Line %d: cannot read alpha orbital energies", lineNumber);
2995 } else if (strcmp(buf, "Alpha MO coefficients") == 0) {
2996 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
2997 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha MO coefficients: %s", lineNumber, tokens[2]);
3001 if (sReadNumberArray(&bset->mo, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3002 s_append_asprintf(errbuf, "Line %d: cannot read MO coefficients", lineNumber);
3006 } else if (strcmp(buf, "Beta Orbital Energies") == 0) {
3007 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
3008 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta orbitals: %s", lineNumber, tokens[2]);
3012 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3013 s_append_asprintf(errbuf, "Line %d: cannot read beta orbital energies", lineNumber);
3017 bset->moenergies = (Double *)realloc(bset->moenergies, sizeof(Double) * 2 * ncomps);
3018 bset->nmos = ncomps * 2;
3019 bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);
3020 memmove(bset->moenergies + ncomps, dary, sizeof(Double) * ncomps);
3021 memset(bset->mo + ncomps * ncomps, 0, sizeof(Double) * ncomps * ncomps);
3024 } else if (strcmp(buf, "Beta MO coefficients") == 0) {
3025 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
3026 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta MO coefficients: %s", lineNumber, tokens[2]);
3030 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3031 s_append_asprintf(errbuf, "Line %d: cannot read alpha MO coefficients", lineNumber);
3035 bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps); /* Should be unnecessary, just in case */
3036 memmove(bset->mo + ncomps * ncomps, dary, sizeof(Double) * ncomps * ncomps);
3039 } else if (strcmp(buf, "Total SCF Density") == 0) {
3040 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * (ncomps + 1) / 2) {
3041 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of SCF densities: %s", lineNumber, tokens[2]);
3045 if (sReadNumberArray(&bset->scfdensities, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3046 s_append_asprintf(errbuf, "Line %d: cannot read SCF densities", lineNumber);
3052 if (mp->natoms == 0) {
3053 s_append_asprintf(errbuf, "Atom information is missing");
3057 if (bset->shells == NULL || bset->priminfos == NULL) {
3058 s_append_asprintf(errbuf, "Gaussian primitive information is missing");
3062 if (bset->mo == NULL) {
3063 s_append_asprintf(errbuf, "MO coefficients were not found");
3067 if (sSetupGaussianCoefficients(bset) != 0) {
3068 s_append_asprintf(errbuf, "Internal error during setup MO calculation");
3081 if (mp->bset != NULL) {
3082 BasisSetRelease(mp->bset);
3088 Panic("low memory while reading fchk file %s", fname);
3089 return -1; /* not reached */
3093 MoleculeLoadGamessDatFile(Molecule *mol, const char *fname, char **errbuf)
3098 int lineNumber, i, j, k, len, natoms = 0;
3105 Vector *vbuf = NULL;
3107 int optimizing = 0, status = 0;
3111 mol = MoleculeNew();
3113 if (mol->natoms == 0)
3116 fp = fopen(fname, "rb");
3118 s_append_asprintf(errbuf, "Cannot open file");
3122 /* ESP is cleared (not undoable!) */
3123 if (mol->elpots != NULL) {
3130 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3133 if (strncmp(buf, " $DATA", 6) == 0) {
3134 /* Initial geometry */
3136 vbuf = (Vector *)calloc(sizeof(Vector), mol->natoms);
3139 ReadLine(buf, sizeof buf, fp, &lineNumber); /* Title */
3140 ReadLine(buf, sizeof buf, fp, &lineNumber); /* Symmetry */
3141 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3142 if (strncmp(buf, " $END", 5) == 0)
3144 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3145 s_append_asprintf(errbuf, "Line %d: bad format in $DATA section", lineNumber);
3151 memset(&a, 0, sizeof(a));
3152 strncpy(a.aname, sval, 4);
3156 a.atomicNumber = (Int)dval[0];
3157 strncpy(a.element, ElementToString(a.atomicNumber, sval), 3);
3158 a.type = AtomTypeEncodeToUInt(a.element);
3159 a.weight = WeightForAtomicNumber(a.atomicNumber);
3160 MoleculeCreateAnAtom(mol, &a, mol->natoms);
3163 if (i >= mol->natoms) {
3164 s_append_asprintf(errbuf, "Line %d: too many atoms", lineNumber);
3168 if ((ap = ATOM_AT_INDEX(mol->atoms, i))->atomicNumber != dval[0]) {
3169 s_append_asprintf(errbuf, "Line %d: atomic number does not match", lineNumber);
3173 vbuf[i].x = dval[1];
3174 vbuf[i].y = dval[2];
3175 vbuf[i].z = dval[3];
3177 /* Skip until a blank line is found */
3178 /* 2013.6.11. Line including "PM3" is also recognized as the end of atom */
3179 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3180 for (j = 0; buf[j] == ' '; j++);
3181 if (buf[j] == '\n' || strncmp(buf + j, "PM3", 3) == 0)
3188 /* Set atom positions */
3190 if (natoms < mol->natoms) {
3191 s_append_asprintf(errbuf, "Line %d: too few atoms", lineNumber);
3195 ig = IntGroupNewWithPoints(0, natoms, -1);
3196 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, natoms, vbuf);
3197 IntGroupRelease(ig);
3200 vbuf = (Vector *)calloc(sizeof(Vector), natoms);
3201 nframes = MoleculeGetNumberOfFrames(mol);
3205 } else if (strstr(buf, "DATA FROM NSERCH") != NULL || (strstr(buf, "RESULTS FROM SUCCESSFUL") != NULL && (n1 = 1))) {
3206 /* Skip until the separator line is read (three or four lines) */
3210 s_append_asprintf(errbuf, "Line %d: the separator line at the top of the coordinates is not found: bad format?", lineNumber);
3214 ReadLine(buf, sizeof buf, fp, &lineNumber);
3215 } while (strstr(buf, "----------------------------") == NULL);
3216 for (i = 0; i < natoms; i++) {
3217 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3218 s_append_asprintf(errbuf, "Unexpected end of file in reading NSERCH data");
3222 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3223 s_append_asprintf(errbuf, "Line %d: bad format in NSERCH coordinate data", lineNumber);
3227 vbuf[i].x = dval[1];
3228 vbuf[i].y = dval[2];
3229 vbuf[i].z = dval[3];
3231 ig = IntGroupNewWithPoints(nframes, 1, -1);
3232 MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, natoms, vbuf, 0, NULL);
3233 IntGroupRelease(ig);
3236 optimizing = 1; /* Flag to skip reading the VEC group */
3240 } else if (strstr(buf, "E(UHF)") != NULL || (strstr(buf, "E(RHF)") != NULL && (n1 = 1)) || (strstr(buf, "E(ROHF)") != NULL && (n1 = 2))) {
3241 if (mol->bset == NULL) {
3242 i = MoleculeAllocateBasisSetRecord(mol, n1, 0, 0);
3244 s_append_asprintf(errbuf, "Line %d: cannot allocate basis set internal buffer", lineNumber);
3249 } else if (strncmp(buf, " $VEC", 5) == 0) {
3251 /* Read the vec group */
3252 if (mol->bset == NULL || mol->bset->ncomps == 0)
3253 continue; /* Just ignore */
3255 continue; /* Ignore VEC group during optimization */
3256 coeffs = (Double *)calloc(sizeof(Double), mol->bset->ncomps);
3257 if (coeffs == NULL) {
3258 s_append_asprintf(errbuf, "Line %d: low memory during $VEC", lineNumber);
3263 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3265 if (strncmp(buf, " $END", 5) == 0)
3267 while ((j = 5 + (k % 5) * 15) <= len && buf[j] != 0 && buf[j] != '\n') {
3268 strncpy(sval, buf + j, 15);
3270 coeffs[k] = strtod(sval, NULL);
3275 if (k < mol->bset->ncomps)
3277 j = MoleculeSetMOCoefficients(mol, i, -1000000, k, coeffs);
3279 s_append_asprintf(errbuf, "Line %d: cannot set coefficients for MO %d", lineNumber, i + 1);
3290 } else if ((strstr(buf, "ELECTRIC POTENTIAL") != NULL || strstr(buf, "ELECTROSTATIC POTENTIAL") != NULL) && strstr(buf, "ELPOTT") != NULL) {
3292 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3294 if (strstr(buf, "TOTAL NUMBER OF GRID POINTS") != NULL)
3296 if (sscanf(buf, "%d %lf %lf %lf %lf", &ival[0], &dval[0], &dval[1], &dval[2], &dval[3]) < 5)
3298 ep = AssignArray(&mol->elpots, &mol->nelpots, sizeof(Elpot), i, NULL);
3299 ep->pos.x = dval[0];
3300 ep->pos.y = dval[1];
3301 ep->pos.z = dval[2];
3306 goto redo; /* This section has no end line, so the last line should be processed again */
3307 else break; /* End of file encountered or interrupted */
3308 } /* TODO: read MOLPLT info if present */
3311 s_append_asprintf(errbuf, "User interrupt at line %d", lineNumber);
3317 if (mol->natoms > 0)
3318 retval = 0; /* Return the partially constructed molecule */
3319 if (newmol && mol->nbonds == 0) {
3322 MoleculeGuessBonds(mol, 1.2, &nbonds, &bonds);
3324 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
3332 MoleculeReadCoordinatesFromFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
3335 if (ftype == NULL || *ftype == 0) {
3337 cp = strrchr(fname, '.');
3341 cp = guessMoleculeType(fname);
3342 if (strcmp(cp, "???") != 0)
3346 if (strcasecmp(ftype, "pdb") == 0) {
3347 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3350 /* Try all formats once again */
3351 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3357 MoleculeReadCoordinatesFromPdbFile(Molecule *mp, const char *fname, char **errbuf)
3363 int i, j, new_unit, retval;
3371 fp = fopen(fname, "rb");
3373 s_append_asprintf(errbuf, "Cannot open file");
3376 /* flockfile(fp); */
3377 if (mp->natoms == 0)
3380 /* Allocate buffer for undo-capable modification */
3381 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
3382 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3383 /* Retain current position if the atom info is missing in the input file */
3389 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3390 if (strncmp(buf, "END", 3) == 0)
3392 if (strncmp(buf, "HETATM", 6) == 0 || strncmp(buf, "ATOM", 4) == 0) {
3394 Int serial, intCharge, resSeq;
3397 char segName[5], resName[4], atomName[5], resSeqStr[5], atomType[3], element[3], occStr[6];
3399 memset(&w, 0, sizeof(w));
3400 ReadFormat(buf, "x6 I5 x1 S4 x1 S3 x1 x1 S4 x1 x3 F8 F8 F8 S6 F6 x6 S4 S2 I2",
3401 &w.serial, w.atomName, w.resName, w.resSeqStr, &w.r.x, &w.r.y, &w.r.z,
3402 w.occStr, &w.temp, w.segName, w.element, &w.intCharge);
3403 if (w.atomName[0] == 0) {
3404 continue; /* Atom name is empty */
3406 /* A workaround for residue number >= 10000 (XPLOR style) */
3407 if (w.resSeqStr[0] >= 'A' && w.resSeqStr[0] <= 'Z') {
3408 w.resSeq = (w.resSeqStr[0] - 'A' + 10) * 1000 + atoi(w.resSeqStr + 1);
3410 w.resSeq = atoi(w.resSeqStr);
3412 if (w.element[0] == 0) {
3413 /* $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl */
3414 for (p = w.atomName; *p != 0; p++) {
3415 if (isalpha(*p) && *p != '_') {
3416 w.element[0] = toupper(*p);
3417 if (isalpha(p[1]) && p[1] != '_') {
3418 w.element[1] = toupper(p[1]);
3427 if (w.occStr[0] == 0)
3430 w.occ = atof(w.occStr);
3431 if (w.serial <= 0) {
3432 s_append_asprintf(errbuf, "line %d: non-positive atom number %d", lineNumber, w.serial);
3436 w.serial--; /* The internal atom number is 0-based */
3437 if (w.serial >= mp->natoms) {
3439 /* Create a new atom entry */
3440 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, w.serial, NULL);
3442 s_append_asprintf(errbuf, "line %d: the atom number %d does not exist in the structure file", lineNumber, w.serial+1);
3448 ap = ATOM_AT_INDEX(mp->atoms, w.serial);
3450 ap->occupancy = w.occ;
3451 ap->tempFactor = w.temp;
3452 if (w.segName[0] == 0)
3453 strncpy(w.segName, "MAIN", 4);
3454 strncpy(ap->segName, w.segName, 4);
3455 ap->resSeq = w.resSeq;
3456 strncpy(ap->resName, w.resName, 4);
3457 strncpy(ap->aname, w.atomName, 4);
3458 strncpy(ap->element, w.element, 2);
3460 ap->atomicNumber = ElementToInt(ap->element);
3461 ap->type = AtomTypeEncodeToUInt(ap->element);
3462 ap->weight = WeightForAtomicNumber(ap->atomicNumber);
3463 ap->intCharge = w.intCharge;
3464 if (ap->resSeq > 0) {
3465 if (ap->resSeq < mp->nresidues) {
3466 /* Update the resName according to residues[] */
3467 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
3469 /* Register the resName to residues[] */
3470 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, w.resName);
3474 strcpy(ap->resName, "XXX");
3475 if (mp->nresidues == 0)
3476 AssignArray(&mp->residues, &mp->nresidues, 4, 0, ap->resName);
3478 i = ElementToInt(ap->element);
3480 ap->weight = gElementParameters[i].weight;
3482 /* Not a new unit: only the atom position is updated */
3486 } else if (strncmp(buf, "CONECT", 6) == 0 && new_unit) {
3487 i = ReadFormat(buf, "x6 I5I5I5I5I5I5I5I5I5I5I5I5",
3488 ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
3489 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7,
3490 ibuf + 8, ibuf + 9, ibuf + 10, ibuf + 11);
3494 for (j = 0; j < i; j++) {
3495 if (ibuf[j] < 0 || ibuf[j] > mp->natoms) {
3496 s_append_asprintf(errbuf, "line %d: The CONECT record contains non-existent atom %d", lineNumber, ibuf[j]);
3499 } else if (ibuf[j] == 0)
3505 for (j = 1, bi = 0; j < i; j++) {
3506 if (ibuf[0] < ibuf[j]) {
3507 if (MoleculeLookupBond(mp, ibuf[0], ibuf[j]) >= 0) {
3508 s_append_asprintf(errbuf, "line %d: warning: duplicate bond %d-%d\n", lineNumber, ibuf[0], ibuf[j]);
3510 bbuf[bi * 2] = ibuf[0] - 1;
3511 bbuf[bi * 2 + 1] = ibuf[j] - 1;
3519 retval = MoleculeAddBonds(mp, bi, bbuf, NULL, 1);
3521 s_append_asprintf(errbuf, "line %d: bad bond specification", lineNumber);
3528 /* funlockfile(fp); */
3531 /* Renumber atoms if some atom number is unoccupied */
3532 int *old2new, oldidx, newidx;
3533 old2new = (int *)calloc(sizeof(int), mp->natoms);
3534 if (old2new == NULL) {
3535 s_append_asprintf(errbuf, "Out of memory");
3539 for (oldidx = newidx = 0; oldidx < mp->natoms; oldidx++) {
3540 ap = ATOM_AT_INDEX(mp->atoms, oldidx);
3541 if (ap->aname[0] != 0) {
3542 old2new[oldidx] = newidx;
3543 if (oldidx > newidx)
3544 memmove(ATOM_AT_INDEX(mp->atoms, newidx), ap, gSizeOfAtomRecord);
3548 mp->natoms = newidx;
3549 if (oldidx > newidx) {
3550 /* Renumber the connects and bonds */
3552 for (i = 0; i < mp->natoms; i++) {
3553 ap = ATOM_AT_INDEX(mp->atoms, i);
3554 cp = AtomConnectData(&ap->connect);
3555 for (j = 0; j < ap->connect.count; j++) {
3556 cp[j] = old2new[cp[j]];
3559 for (i = 0; i < mp->nbonds * 2; i++) {
3560 mp->bonds[i] = old2new[mp->bonds[i]];
3563 retval = MoleculeRebuildTablesFromConnects(mp);
3565 /* This error may not happen */
3566 s_append_asprintf(errbuf, "Cannot build angle/dihedral/improper tables");
3570 /* Undo action: delete all atoms */
3573 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3574 act = MolActionNew(gMolActionUnmergeMolecule, ig);
3575 act->frame = mp->cframe;
3576 MolActionCallback_registerUndo(mp, act);
3577 MolActionRelease(act);
3578 IntGroupRelease(ig);
3581 /* Set the new atom positions */
3582 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3583 MolActionCreateAndPerform(mp, gMolActionSetAtomPositions, ig, mp->natoms, vp);
3584 IntGroupRelease(ig);
3588 mp->nframes = -1; /* Should be recalculated later */
3590 return 1; /* No atoms */
3594 /* funlockfile(fp); */
3600 return 1; /* Maybe different format? */
3605 MoleculeReadCoordinatesFromDcdFile(Molecule *mp, const char *fname, char **errbuf)
3608 SFloat32 *xp, *yp, *zp;
3611 int n, errcount = 0;
3613 if (mp == NULL || mp->natoms == 0) {
3614 s_append_asprintf(errbuf, "Molecule is empty");
3617 n = DcdOpen(fname, &dcd);
3620 case -2: s_append_asprintf(errbuf, "Cannot open file"); break;
3621 case 1: s_append_asprintf(errbuf, "Premature EOF encountered"); break;
3622 case 2: s_append_asprintf(errbuf, "Bad block length of the first section"); break;
3623 case 3: s_append_asprintf(errbuf, "\"CORD\" signature is missing"); break;
3624 case 4: s_append_asprintf(errbuf, "Bad termination of the first section"); break;
3625 case 5: s_append_asprintf(errbuf, "The title section is not correct"); break;
3626 case 6: s_append_asprintf(errbuf, "The atom number section is not correct"); break;
3627 default: s_append_asprintf(errbuf, "Read error in dcd file"); break;
3631 if (dcd.natoms == 0) {
3632 s_append_asprintf(errbuf, "No atoms were found in the dcd file");
3634 } else if (dcd.nframes == 0) {
3635 s_append_asprintf(errbuf, "No frames were found in the dcd file");
3645 vp = (Vector *)calloc(sizeof(Vector), mp->natoms * dcd.nframes);
3647 cp = (Vector *)calloc(sizeof(Vector), dcd.nframes * 4);
3649 xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3650 yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3651 zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3652 ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mp), dcd.nframes, -1);
3653 if (vp == NULL || xp == NULL || yp == NULL || zp == NULL || ig == NULL) {
3654 s_append_asprintf(errbuf, "Cannot allocate memory");
3660 if (ig) IntGroupRelease(ig);
3663 for (n = 0; n < dcd.nframes; n++) {
3666 SFloat32 dcdcell[6];
3667 if (DcdReadFrame(&dcd, n, xp, yp, zp, dcdcell)) {
3668 s_append_asprintf(errbuf, "Read error in dcd file");
3671 for (i = 0, vpp = &vp[n * mp->natoms]; i < dcd.natoms && i < mp->natoms; i++, vpp++) {
3679 /* dcdcell = {a, gamma, b, beta, alpha, c} */
3680 /* angles are described either in cosines (Charmm and NAMD > 2.5) or degrees (NAMD 2.5) */
3681 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) {
3682 dcdcell[4] = cos(dcdcell[4] * kDeg2Rad); /* cos(alpha) */
3683 dcdcell[3] = cos(dcdcell[3] * kDeg2Rad); /* cos(beta) */
3684 dcdcell[1] = cos(dcdcell[1] * kDeg2Rad); /* cos(gamma) */
3686 /* a axis lies along the cartesian x axis */
3687 sing = sqrt(1 - dcdcell[1] * dcdcell[1]);
3688 vpp[0].x = dcdcell[0];
3691 vpp[1].x = dcdcell[2] * dcdcell[1];
3692 vpp[1].y = dcdcell[2] * sing;
3694 vpp[2].x = dcdcell[5] * dcdcell[3];
3695 vpp[2].y = dcdcell[5] * (dcdcell[4] - dcdcell[3] * dcdcell[1]) / sing;
3696 vpp[2].z = sqrt(dcdcell[5] * dcdcell[5] - vpp[2].x * vpp[2].x - vpp[2].y * vpp[2].y);
3697 vpp[3].x = vpp[3].y = vpp[3].z = 0.0;
3698 if (mp->cell == NULL) {
3699 /* Create periodicity if not present */
3700 MolActionCreateAndPerform(mp, gMolActionSetBox, &vpp[0], &vpp[1], &vpp[2], &vpp[3], 7, 0);
3704 if (MolActionCreateAndPerform(mp, gMolActionInsertFrames, ig, mp->natoms * dcd.nframes, vp, (cp == NULL ? 0 : dcd.nframes * 4), cp) != 0)
3705 s_append_asprintf(errbuf, "Cannot insert frames");
3706 mp->startStep = dcd.nstart;
3707 mp->stepsPerFrame = dcd.ninterval;
3708 mp->psPerStep = dcd.delta;
3717 IntGroupRelease(ig);
3724 MoleculeReadExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
3735 fp = fopen(fname, "rb");
3737 s_append_asprintf(errbuf, "Cannot open file");
3743 flags[0] = flags[1] = flags[2] = 0;
3744 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3745 if (strncmp(buf, "Bounding box:", 13) == 0) {
3746 for (i = 0; i < 3; i++) {
3747 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3748 s_append_asprintf(errbuf, "line %d: missing %d component of the bounding box", lineNumber, i + 1);
3752 n = sscanf(buf, "%lf %lf %lf %d", &d[0], &d[1], &d[2], &flag);
3754 vv.x = vv.y = vv.z = 0.0;
3756 case 0: vv.x = d[0]; break;
3757 case 1: vv.y = d[0]; break;
3758 case 2: vv.z = d[0]; break;
3760 if (n == 1 || (n == 2 && d[1] != 0.0))
3767 flags[i] = (flag != 0);
3769 flags[i] = (VecLength2(vv) != 0);
3773 if (mp->cell != NULL)
3774 vv = mp->cell->origin;
3776 vv.x = vv.y = vv.z = 0.0;
3777 MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
3778 } else if (strncmp(buf, "Bounding box origin:", 20) == 0) {
3779 if (mp->cell != NULL) {
3780 v[0] = mp->cell->axes[0];
3781 v[1] = mp->cell->axes[1];
3782 v[2] = mp->cell->axes[2];
3783 memmove(flags, mp->cell->flags, 3);
3785 v[0].x = 1.0; v[0].y = v[0].z = 0.0;
3786 v[1].y = 1.0; v[1].x = v[1].z = 0.0;
3787 v[2].z = 1.0; v[2].x = v[2].y = 0.0;
3788 flags[0] = flags[1] = flags[2] = 1.0;
3790 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0 || (n = sscanf(buf, "%lf %lf %lf", &d[0], &d[1], &d[2]) < 3)) {
3791 s_append_asprintf(errbuf, "line %d: wrong format for the bounding box origin", lineNumber);
3798 MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
3810 MoleculeWriteToFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
3814 if (ftype == NULL || *ftype == 0) {
3816 cp = strrchr(fname, '.');
3820 cp = guessMoleculeType(fname);
3821 if (strcmp(cp, "???") != 0)
3825 if (strcasecmp(ftype, "psf") == 0) {
3826 retval = MoleculeWriteToPsfFile(mp, fname, errbuf);
3827 } else if (strcasecmp(ftype, "pdb") == 0) {
3828 retval = MoleculeWriteToPdbFile(mp, fname, errbuf);
3829 } else if (strcasecmp(ftype, "tep") == 0) {
3830 retval = MoleculeWriteToTepFile(mp, fname, errbuf);
3832 s_append_asprintf(errbuf, "The file format should be specified");
3836 MoleculeSetPath(mp, fname);
3841 MoleculeWriteToMbsfFile(Molecule *mp, const char *fname, char **errbuf)
3844 Int i, j, k, n1, n2, n3, n_aniso, nframes, nanchors, n_uff;
3849 fp = fopen(fname, "wb");
3851 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
3856 nframes = MoleculeFlushFrames(mp);
3858 fprintf(fp, "!:atoms\n");
3859 fprintf(fp, "! idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge\n");
3860 n1 = n2 = n3 = n_aniso = nanchors = n_uff = 0;
3861 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3862 strncpy(bufs[0], ap->segName, 4);
3864 strncpy(bufs[1], ap->resName, 4);
3866 strncpy(bufs[2], ap->aname, 4);
3868 AtomTypeDecodeToString(ap->type, bufs[3]);
3870 strncpy(bufs[4], ap->element, 4);
3872 for (j = 0; j < 5; j++) {
3873 if (bufs[j][0] == 0) {
3877 for (k = 0; k < 6; k++) {
3878 if (bufs[j][k] == 0)
3880 if (bufs[j][k] > 0 && bufs[j][k] < ' ')
3884 if (SYMOP_ALIVE(ap->symop))
3886 if (ap->fix_force != 0)
3888 if (ap->mm_exclude || ap->periodic_exclude)
3890 if (ap->aniso != NULL)
3892 if (ap->anchor != NULL)
3894 if (ap->uff_type[0] != 0)
3896 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);
3901 fprintf(fp, "!:uff_type\n");
3902 fprintf(fp, "! idx uff_type\n");
3903 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3904 fprintf(fp, "%d %.5s\n", i, ap->uff_type);
3910 fprintf(fp, "!:atoms_symop\n");
3911 fprintf(fp, "! idx symop symbase\n");
3912 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3914 n = ap->symop.sym * 1000000 + ap->symop.dx * 10000 + ap->symop.dy * 100 + ap->symop.dz;
3915 fprintf(fp, "%d %d %d\n", i, n, ap->symbase);
3921 fprintf(fp, "!:atoms_fix\n");
3922 fprintf(fp, "! idx fix_force fix_pos\n");
3923 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3924 fprintf(fp, "%d %f %f %f %f\n", i, ap->fix_force, ap->fix_pos.x, ap->fix_pos.y, ap->fix_pos.z);
3930 fprintf(fp, "!:mm_exclude\n");
3931 fprintf(fp, "! idx mm_exclude periodic_exclude\n");
3932 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3933 fprintf(fp, "%d %d %d\n", i, ap->mm_exclude, ap->periodic_exclude);
3939 fprintf(fp, "!:pi_anchor\n");
3940 fprintf(fp, "! idx count; n1 weight1; n2 weight2; ...; nN weightN\n");
3941 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3943 if (ap->anchor == NULL)
3945 k = ap->anchor->connect.count;
3946 ip = AtomConnectData(&ap->anchor->connect);
3947 fprintf(fp, "%d %d\n", i, k);
3948 for (j = 0; j < k; j++) {
3949 fprintf(fp, "%d %f\n", ip[j], ap->anchor->coeffs[j]);
3960 for (i = 0; (i == n2 || i < n1); i++) {
3961 fprintf(fp, "!:positions ; frame %d\n", i);
3962 fprintf(fp, "! idx x y z [sx sy sz]\n");
3963 for (j = 0, ap = mp->atoms; j < mp->natoms; j++, ap = ATOM_NEXT(ap)) {
3966 if (i != n2 && i < ap->nframes)
3967 vp = ap->frames + i;
3970 if (ap->sigma.x != 0.0 || ap->sigma.y != 0.0 || ap->sigma.z != 0.0)
3973 fprintf(fp, "%d %.8f %.8f %.8f", j, vp->x, vp->y, vp->z);
3975 fprintf(fp, " %.8f %.8f %.8f", ap->sigma.x, ap->sigma.y, ap->sigma.z);
3982 if (mp->nbonds > 0) {
3983 fprintf(fp, "!:bonds\n");
3984 fprintf(fp, "! from1 to1 from2 to2 from3 to3 from4 to4\n");
3985 for (i = 0; i < mp->nbonds; i++) {
3986 fprintf(fp, "%d %d%c", mp->bonds[i * 2], mp->bonds[i * 2 + 1], (i % 4 == 3 || i == mp->nbonds - 1 ? '\n' : ' '));
3991 if (mp->nbondOrders > 0) {
3992 fprintf(fp, "!:bond_orders\n");
3993 fprintf(fp, "! order1 order2 order3 order4\n");
3994 for (i = 0; i < mp->nbondOrders; i++) {
3995 fprintf(fp, "%.6f%c", mp->bondOrders[i], (i % 4 == 3 || i == mp->nbondOrders - 1 ? '\n' : ' '));
4000 if (mp->nangles > 0) {
4001 fprintf(fp, "!:angles\n");
4002 fprintf(fp, "! a1 b1 c1 a2 b2 c2 a3 b3 c3\n");
4003 for (i = 0; i < mp->nangles; i++) {
4004 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' : ' '));
4009 if (mp->ndihedrals > 0) {
4010 fprintf(fp, "!:dihedrals\n");
4011 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
4012 for (i = 0; i < mp->ndihedrals; i++) {
4013 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' : ' '));
4018 if (mp->nimpropers > 0) {
4019 fprintf(fp, "!:impropers\n");
4020 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
4021 for (i = 0; i < mp->nimpropers; i++) {
4022 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' : ' '));
4027 if (mp->cell != NULL) {
4028 fprintf(fp, "!:xtalcell\n");
4029 fprintf(fp, "! a b c alpha beta gamma\n");
4030 fprintf(fp, "! This information is redundant and overridden by the following periodic_box info\n");
4031 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]);
4034 fprintf(fp, "!:periodic_box\n");
4035 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");
4036 for (i = 0; i < 3; i++)
4037 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->axes[i].x, mp->cell->axes[i].y, mp->cell->axes[i].z);
4038 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->origin.x, mp->cell->origin.y, mp->cell->origin.z);
4039 fprintf(fp, "%d %d %d%s\n", mp->cell->flags[0], mp->cell->flags[1], mp->cell->flags[2], (mp->cell->has_sigma ? " 1" : ""));
4040 if (mp->cell->has_sigma) {
4041 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]);
4046 if (mp->nframe_cells > 0) {
4047 fprintf(fp, "!:frame_periodic_boxes\n");
4048 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz\n");
4049 for (i = 0; i < mp->nframe_cells * 4; i++) {
4050 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->frame_cells[i].x, mp->frame_cells[i].y, mp->frame_cells[i].z);
4055 if (mp->nsyms > 0) {
4056 fprintf(fp, "!:symmetry_operations\n");
4057 fprintf(fp, "! a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3\n");
4058 for (i = 0; i < mp->nsyms; i++) {
4059 Transform *tp = mp->syms + i;
4060 const unsigned char s_index_order[12] = {0, 3, 6, 1, 4, 7, 2, 5, 8, 9, 10, 11};
4061 for (j = 0; j < 12; j++)
4062 fprintf(fp, "%11.6f%c", (*tp)[s_index_order[j]], (j % 3 == 2 ? '\n' : ' '));
4068 fprintf(fp, "!:anisotropic_thermal_parameters\n");
4069 fprintf(fp, "! b11 b22 b33 b12 b13 b23 [sigma; sb11 sb22 sb33 sb12 sb13 sb23]\n");
4070 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4071 if (ap->aniso != NULL) {
4072 Double *bp = ap->aniso->bij;
4073 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" : ""));
4074 if (ap->aniso->has_bsig) {
4075 bp = ap->aniso->bsig;
4076 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]);
4079 fprintf(fp, "0 0 0 0 0 0\n");
4085 if (mp->arena != NULL) {
4086 MDArena *arena = mp->arena;
4087 fprintf(fp, "!:md_parameters\n");
4088 fprintf(fp, "log_file %s\n", arena->log_result_name);
4089 fprintf(fp, "coord_file %s\n", arena->coord_result_name);
4090 fprintf(fp, "vel_file %s\n", arena->vel_result_name);
4091 fprintf(fp, "force_file %s\n", arena->force_result_name);
4092 fprintf(fp, "debug_file %s\n", arena->debug_result_name);
4093 fprintf(fp, "debug_output_level %d\n", arena->debug_output_level);
4094 fprintf(fp, "step %d\n", arena->step);
4095 fprintf(fp, "coord_output_freq %d\n", arena->coord_output_freq);
4096 fprintf(fp, "energy_output_freq %d\n", arena->energy_output_freq);
4097 fprintf(fp, "coord_frame %d\n", arena->coord_result_frame);
4098 fprintf(fp, "timestep %g\n", arena->timestep);
4099 fprintf(fp, "cutoff %g\n", arena->cutoff);
4100 fprintf(fp, "electro_cutoff %g\n", arena->electro_cutoff);
4101 fprintf(fp, "pairlist_distance %g\n", arena->pairlist_distance);
4102 fprintf(fp, "switch_distance %g\n", arena->switch_distance);
4103 fprintf(fp, "temperature %g\n", arena->temperature);
4104 fprintf(fp, "andersen_freq %d\n", arena->andersen_thermo_freq);
4105 fprintf(fp, "andersen_coupling %g\n", arena->andersen_thermo_coupling);
4106 fprintf(fp, "random_seed %d\n", arena->random_seed);
4107 fprintf(fp, "dielectric %g\n", arena->dielectric);
4108 fprintf(fp, "gradient_convergence %g\n", arena->gradient_convergence);
4109 fprintf(fp, "coordinate_convergence %g\n", arena->coordinate_convergence);
4110 fprintf(fp, "use_xplor_shift %d\n", arena->use_xplor_shift);
4111 fprintf(fp, "scale14_vdw %g\n", arena->scale14_vdw);
4112 fprintf(fp, "scale14_elect %g\n", arena->scale14_elect);
4113 fprintf(fp, "relocate_center %d\n", arena->relocate_center);
4114 fprintf(fp, "surface_probe_radius %g\n", arena->probe_radius);
4115 fprintf(fp, "surface_tension %g\n", arena->surface_tension);
4116 fprintf(fp, "surface_potential_freq %d\n", arena->surface_potential_freq);
4117 fprintf(fp, "use_graphite %d\n", arena->use_graphite);
4118 fprintf(fp, "alchemical_lambda %g\n", arena->alchem_lambda);
4119 fprintf(fp, "alchemical_delta_lambda %g\n", arena->alchem_dlambda);
4120 if (arena->nalchem_flags > 0) {
4121 fprintf(fp, "alchem_flags %d", arena->nalchem_flags);
4122 for (i = 0; i < arena->nalchem_flags; i++) {
4125 else if (i % 10 == 0)
4127 fputc('0' + arena->alchem_flags[i], fp);
4131 if (arena->pressure != NULL) {
4133 fprintf(fp, "pressure_freq %d\n", arena->pressure->freq);
4134 fprintf(fp, "pressure_coupling %g\n", arena->pressure->coupling);
4135 dp = arena->pressure->apply;
4136 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]);
4137 dp = arena->pressure->cell_flexibility;
4138 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]);
4139 fprintf(fp, "pressure_fluctuate_cell_origin %g\n", arena->pressure->fluctuate_cell_origin);
4140 fprintf(fp, "pressure_fluctuate_cell_orientation %g\n", arena->pressure->fluctuate_cell_orientation);
4144 if (mp->par != NULL) {
4145 Parameter *par = mp->par;
4146 fprintf(fp, "!:parameters\n");
4147 ParameterAppendToFile(par, fp);
4151 fprintf(fp, "!:velocity\n");
4152 fprintf(fp, "! idx vx vy vz\n");
4153 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4154 fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->v.x, ap->v.y, ap->v.z);
4158 fprintf(fp, "!:force\n");
4159 fprintf(fp, "! idx fx fy fz\n");
4160 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4161 fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->f.x, ap->f.y, ap->f.z);
4166 if (mp->mview != NULL) {
4168 if (mp->mview->track != NULL) {
4169 fprintf(fp, "!:trackball\n");
4170 fprintf(fp, "! scale; trx try trz; theta_deg x y z\n");
4171 f[0] = TrackballGetScale(mp->mview->track);
4172 fprintf(fp, "%f\n", f[0]);
4173 TrackballGetTranslate(mp->mview->track, f);
4174 fprintf(fp, "%f %f %f\n", f[0], f[1], f[2]);
4175 TrackballGetRotate(mp->mview->track, f);
4176 fprintf(fp, "%f %f %f %f\n", f[0], f[1], f[2], f[3]);
4179 fprintf(fp, "!:view\n");
4180 fprintf(fp, "show_unit_cell %d\n", mp->mview->showUnitCell);
4181 fprintf(fp, "show_periodic_box %d\n", mp->mview->showPeriodicBox);
4182 fprintf(fp, "show_expanded_atoms %d\n", mp->mview->showExpandedAtoms);
4183 fprintf(fp, "show_ellipsoids %d\n", mp->mview->showEllipsoids);
4184 fprintf(fp, "show_hydrogens %d\n", mp->mview->showHydrogens);
4185 fprintf(fp, "show_dummy_atoms %d\n", mp->mview->showDummyAtoms);
4186 fprintf(fp, "show_rotation_center %d\n", mp->mview->showRotationCenter);
4187 fprintf(fp, "show_graphite_flag %d\n", mp->mview->showGraphiteFlag);
4188 fprintf(fp, "show_graphite %d\n", mp->mview->showGraphite);
4189 fprintf(fp, "show_periodic_image_flag %d\n", mp->mview->showPeriodicImageFlag);
4190 fprintf(fp, "show_periodic_image %d %d %d %d %d %d\n",
4191 mp->mview->showPeriodicImage[0], mp->mview->showPeriodicImage[1],
4192 mp->mview->showPeriodicImage[2], mp->mview->showPeriodicImage[3],
4193 mp->mview->showPeriodicImage[4], mp->mview->showPeriodicImage[5]);
4194 if (mp->mview->atomRadius != 0.2)
4195 fprintf(fp, "atom_radius %f\n", mp->mview->atomRadius);
4196 if (mp->mview->bondRadius != 0.1)
4197 fprintf(fp, "bond_radius %f\n", mp->mview->bondRadius);
4198 if (mp->mview->atomResolution != 12)
4199 fprintf(fp, "atom_resolution %d\n", mp->mview->atomResolution);
4200 if (mp->mview->bondResolution != 8)
4201 fprintf(fp, "bond_resolution %d\n", mp->mview->bondResolution);
4210 MoleculeWriteToPsfFile(Molecule *mp, const char *fname, char **errbuf)
4216 fp = fopen(fname, "wb");
4218 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4221 fprintf(fp, "PSF\n\n");
4222 fprintf(fp, " 1 !NTITLE\n");
4223 fprintf(fp, " REMARKS FILENAME=\n");
4227 fprintf(fp, "%8d !NATOM\n", mp->natoms);
4228 for (i = 0; i < mp->natoms; i++) {
4230 ap = ATOM_AT_INDEX(mp->atoms, i);
4231 fprintf(fp, "%8d ", i + 1);
4232 if (ap->resSeq >= 10000) {
4233 fmt = "%-3.3s %-5d ";
4235 fmt = "%-4.4s %-4d ";
4237 fprintf(fp, fmt, ap->segName, ap->resSeq);
4238 fprintf(fp, "%-3.3s %-4.4s %-4.4s %12.6f %8.4f 0\n",
4239 ap->resName, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->charge, ap->weight);
4244 fprintf(fp, "%8d !NBOND: bonds\n", mp->nbonds);
4245 for (i = 0; i < mp->nbonds * 2; i++) {
4246 fprintf(fp, "%8d", mp->bonds[i] + 1);
4255 fprintf(fp, "%8d !NTHETA: angles\n", mp->nangles);
4256 for (i = 0; i < mp->nangles * 3; i++) {
4257 fprintf(fp, "%8d", mp->angles[i] + 1);
4266 fprintf(fp, "%8d !NPHI: dihedrals\n", mp->ndihedrals);
4267 for (i = 0; i < mp->ndihedrals * 4; i++) {
4268 fprintf(fp, "%8d", mp->dihedrals[i] + 1);
4277 fprintf(fp, "%8d !NIMPHI: impropers\n", mp->nimpropers);
4278 for (i = 0; i < mp->nimpropers * 4; i++) {
4279 fprintf(fp, "%8d", mp->impropers[i] + 1);
4287 fprintf(fp, "%8d !NDON: donors\n\n", 0);
4288 fprintf(fp, "%8d !NACC: acceptors\n\n", 0);
4289 fprintf(fp, "%8d !NNB: non-bonding exclusions\n\n", 0);
4290 for (i = 0; i < mp->natoms; i++) {
4291 fprintf(fp, "%8d", 0);
4298 fprintf(fp, "%8d !NGRP: groups\n", 1);
4299 fprintf(fp, " 0 0 0\n");
4303 if (i > 5 && strcmp(fname + i - 5, ".psfx") == 0) {
4304 /* Extended psf (with coordinates and other info) */
4305 fprintf(fp, "%8d !COORD: coordinates\n", mp->natoms);
4306 for (i = 0; i < mp->natoms; i++) {
4308 ap = ATOM_AT_INDEX(mp->atoms, i);
4310 fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->aname);
4320 MoleculeWriteToPdbFile(Molecule *mp, const char *fname, char **errbuf)
4326 fp = fopen(fname, "wb");
4328 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4331 for (i = 0; i < mp->natoms; i++) {
4333 ap = ATOM_AT_INDEX(mp->atoms, i);
4334 if (ap->resSeq >= 10000) {
4335 snprintf(buf, sizeof buf, "%c%03d", 'A' + (ap->resSeq - 10000) / 1000, ap->resSeq % 1000);
4337 snprintf(buf, sizeof buf, "%4d", ap->resSeq);
4339 fprintf(fp, "ATOM %5d %-4.4s%1.1s%-3.3s %1.1s%4.4s%1.1s "
4340 "%8.3f%8.3f%8.3f %5.2f %5.2f "
4341 "%-4.4s%-2.2s%-2d\n",
4342 i + 1, ap->aname, " ", ap->resName, " ", buf, " ",
4343 ap->r.x, ap->r.y, ap->r.z, ap->occupancy, ap->tempFactor,
4344 ap->segName, ap->element, ap->intCharge);
4346 for (i = 0; i < mp->natoms; i++) {
4348 ap = ATOM_AT_INDEX(mp->atoms, i);
4349 cp = AtomConnectData(&ap->connect);
4350 for (j = 0; j < ap->connect.count; j++) {
4354 fprintf(fp, "CONECT%5d", i + 1);
4356 fprintf(fp, "%5d", cp[j] + 1);
4361 fprintf(fp, "END\n");
4367 MoleculeWriteToDcdFile(Molecule *mp, const char *fname, char **errbuf)
4370 SFloat32 *xp, *yp, *zp;
4373 if (mp == NULL || mp->natoms == 0) {
4374 s_append_asprintf(errbuf, "Molecule is empty");
4377 memset(&dcd, 0, sizeof(dcd));
4378 dcd.natoms = mp->natoms;
4379 dcd.nframes = MoleculeGetNumberOfFrames(mp);
4380 if (dcd.nframes == 0) {
4381 s_append_asprintf(errbuf, "no frame is present");
4384 dcd.nstart = mp->startStep;
4385 dcd.ninterval = mp->stepsPerFrame;
4386 if (dcd.ninterval == 0)
4388 dcd.nend = dcd.nstart + (dcd.nframes - 1) * dcd.ninterval;
4389 if (mp->cell != NULL)
4391 dcd.delta = mp->psPerStep;
4392 if (dcd.delta == 0.0)
4395 n = DcdCreate(fname, &dcd);
4398 s_append_asprintf(errbuf, "Cannot create dcd file");
4400 s_append_asprintf(errbuf, "Cannot write dcd header");
4405 xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4406 yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4407 zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4408 if (xp == NULL || yp == NULL || zp == NULL) {
4409 s_append_asprintf(errbuf, "Cannot allocate memory");
4416 for (n = 0; n < dcd.nframes; n++) {
4419 for (i = 0, ap = mp->atoms; i < dcd.natoms && mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4421 if (ap->frames == NULL || n >= ap->nframes)
4429 if (i < dcd.natoms) {
4430 size_t sz = (dcd.natoms - i) * sizeof(SFloat32);
4431 memset(xp + i, 0, sz);
4432 memset(yp + i, 0, sz);
4433 memset(zp + i, 0, sz);
4435 if (n < mp->nframe_cells && mp->frame_cells != NULL) {
4436 Vector *cp = &(mp->frame_cells[n * 4]);
4437 dcd.globalcell[0] = VecLength(cp[0]);
4438 dcd.globalcell[2] = VecLength(cp[1]);
4439 dcd.globalcell[5] = VecLength(cp[2]);
4440 dcd.globalcell[1] = VecDot(cp[0], cp[1]) / (dcd.globalcell[0] * dcd.globalcell[2]);
4441 dcd.globalcell[3] = VecDot(cp[0], cp[2]) / (dcd.globalcell[0] * dcd.globalcell[5]);
4442 dcd.globalcell[4] = VecDot(cp[1], cp[2]) / (dcd.globalcell[2] * dcd.globalcell[5]);
4444 if (DcdWriteFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
4445 s_append_asprintf(errbuf, "Write error in dcd file");
4461 MoleculeWriteExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
4467 fp = fopen(fname, "wb");
4469 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4472 if (mp->cell != NULL) {
4473 fprintf(fp, "Bounding box:\n");
4474 for (i = 0; i < 3; i++) {
4475 v = mp->cell->axes[i];
4476 fprintf(fp, "%.3f %.3f %.3f %d\n", v.x, v.y, v.z, mp->cell->flags[i]);
4478 fprintf(fp, "Bounding box origin:\n");
4479 v = mp->cell->origin;
4480 fprintf(fp, "%.3f %.3f %.3f\n", v.x, v.y, v.z);
4487 sCompareByElement(const void *ap, const void *bp)
4489 return ((*(Atom **)bp)->atomicNumber - (*(Atom **)ap)->atomicNumber);
4493 sMakeAdc(int n, int base, Symop symop)
4496 if (SYMOP_ALIVE(symop)) {
4498 sym = (symop.dx + 5) * 10000 + (symop.dy + 5) * 1000 + (symop.dz + 5) * 100 + symop.sym + 1;
4503 return (an + 1) * 100000 + sym;
4507 sCompareAdc(const void *ap, const void *bp)
4509 int n = *((Int *)ap) % 100000 - *((Int *)bp) % 100000;
4511 n = *((Int *)ap) / 100000 - *((Int *)bp) / 100000;
4516 sOutputAtomListInstructions(FILE *fp, int natoms, Atom *atoms)
4518 int i, j, k, an, sym;
4521 adc = (Int *)malloc(sizeof(Int) * natoms);
4524 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4525 if (ap->exflags & kAtomHiddenFlag)
4527 adc[i] = sMakeAdc(i, ap->symbase, ap->symop);
4529 mergesort(adc, natoms, sizeof(Int), sCompareAdc);
4531 /* Create the atom list */
4533 for (i = j = k = 0; i < natoms; i++) {
4534 int an1 = adc[i] / 100000;
4535 int sym1 = adc[i] % 100000;
4536 if (sym == sym1 && an1 == an + 1) {
4543 /* Output the last atom with a minus sign */
4544 adc[j++] = -(an * 100000 + sym);
4545 /* Output this atom */
4552 adc[j++] = -(an * 100000 + sym);
4554 /* Create the instruction cards */
4555 for (i = k = 0; i < j; i++) {
4557 fprintf(fp, " 401");
4558 fprintf(fp, "%9d", adc[i]);
4560 if (i == j - 1 || k == 6 || (k == 5 && i < j - 2 && adc[i + 2] < 0)) {
4569 sEllipsoidType(int an)
4571 return (an >= 18 ? 3 : (an >= 2 && an != 6 ? 2 : (an > 0 ? 1 : 0)));
4575 sOutputAtomTypeInstructions(FILE *fp, int natoms, Atom *atoms)
4579 int etype, elast, istart, ilast, n1, n2;
4580 elast = istart = ilast = -1;
4581 for (i = 0, ap = atoms; i <= natoms; i++, ap++) {
4583 if (SYMOP_ALIVE(ap->symop))
4585 if (ap->exflags & kAtomHiddenFlag)
4587 etype = sEllipsoidType(ap->atomicNumber);
4592 } else if (elast == etype && ilast == i - 1) {
4597 /* Output the instruction card for the 'last' block of atoms */
4600 n1 = 4; n2 = 0; break;
4602 n1 = 4; n2 = 5; break;
4604 n1 = 1; n2 = 0; break;
4606 fprintf(fp, " 1 715 %8d 0 %8d 0 0.100 0.000 0.000\n", n1, n2);
4607 fprintf(fp, " %9d%9d\n", istart + 1, ilast + 1);
4614 sCompareBondType(const void *ap, const void *bp)
4616 /* Descending order */
4617 return *((int *)bp) - *((int *)ap);
4621 sOutputBondInstructions(FILE *fp, int natoms, Atom *atoms, int overlap_correction)
4625 int i, j, n[5], an, count, n1, n2, k;
4629 static const float sBondRad[4] = {0.060, 0.060, 0.060, 0.040};
4630 static const int sBondShade[4] = {5, 3, 1, 1};
4632 n[0] = n[1] = n[2] = n[3] = 0; /* Start index of 3rd row atoms (and higher), 2nd row, 1st row, and H */
4634 for (i = natoms - 1, ap = atoms + i; i >= 0; i--, ap--) {
4635 an = ap->atomicNumber;
4647 if (overlap_correction)
4648 strcpy(buf, " 2 1001 0.000\n");
4650 strcpy(buf, " 2 812\n");
4652 for (i = 0; i < 4; i++) {
4653 for (j = i; j < 4; j++) {
4654 /* Examine bonds between "group i" and "group j" */
4657 double min_bond = 10000.0; /* Minimum distance between bound atoms */
4658 double min_nonbond = 10000.0; /* Minimum distance between non-bound atoms */
4659 double max_bond = -10000.0; /* Maximum distance between bound atoms */
4660 int count_exbond = 0; /* Number of explicit bonds in this group */
4661 for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
4662 for (n2 = n[j], ap2 = atoms + n2; n2 < n[j + 1]; n2++, ap2++) {
4665 VecSub(dr, ap->r, ap2->r);
4667 cp = AtomConnectData(&ap->connect);
4668 for (k = ap->connect.count - 1; k >= 0; k--) {
4673 /* n1 and n2 are bound */
4679 /* n1 and n2 are not bound */
4680 if (d < min_nonbond)
4685 if (min_bond == 10000.0)
4686 continue; /* No bonds between these groups */
4688 if (max_bond + 0.002 < min_nonbond)
4691 max_bond = min_nonbond - 0.002;
4692 /* Some bonds may be omitted, so scan all bonds again */
4693 for (n1 = n[i], ap = ATOM_AT_INDEX(atoms, n1); n1 < n[i + 1]; n1++, ap = ATOM_NEXT(ap)) {
4694 cp = AtomConnectData(&ap->connect);
4695 for (k = ap->connect.count - 1; k >= 0; k--) {
4697 if (n2 < n[j] || n2 >= n[j + 1])
4700 VecSub(dr, ap->r, ap2->r);
4703 /* This bond should be explicitly defined */
4705 if (count_exbond == 0) {
4706 adc1 = -(i + 1); /* Bond type */
4707 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4709 adc1 = sMakeAdc(n1, ap->symbase, ap->symop);
4710 adc2 = sMakeAdc(n2, ap2->symbase, ap2->symop);
4711 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4712 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc2);
4718 /* Output the last instruction card */
4720 /* Make a new trailer card */
4721 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]);
4726 /* Output the last trailer card */
4731 if (count == 0 && overlap_correction) {
4732 /* 1001 card is not yet written, so write it */
4736 snprintf(buf, sizeof(buf), " 1 %3d", (overlap_correction ? 821 : 811));
4737 k = -exbonds[0] - 1; /* Bond type for the first block */
4738 i = 1; /* Index for exbonds[] */
4739 j = 0; /* Count in this block */
4740 while (i <= nexbonds) {
4741 if (j >= 29 || i == nexbonds || exbonds[i] < 0) {
4745 /* The trailer card */
4746 fprintf(fp, " %3d %6.3f\n", sBondShade[k], sBondRad[k]);
4750 k = -exbonds[i++] - 1; /* The new bond type */
4752 } else if (j > 0 && j % 3 == 0) {
4758 snprintf(buf + 9 + (j % 3) * 18, sizeof(buf) - 9 - (j % 3) * 18, "%9d%9d\n", n1, n2);
4766 MoleculeWriteToTepFile(Molecule *mp, const char *fname, char **errbuf)
4769 int i, j, natoms, *ip;
4771 Atom *ap, *atoms, **app;
4773 static Double sUnit[] = {1, 1, 1, 90, 90, 90};
4777 /* Create sorted array of atoms */
4778 natoms = mp->natoms;
4779 atoms = (Atom *)calloc(sizeof(Atom), natoms);
4780 app = (Atom **)calloc(sizeof(Atom *), natoms);
4781 ip = (int *)calloc(sizeof(int), natoms);
4782 if (atoms == NULL || app == NULL || ip == NULL) {
4783 s_append_asprintf(errbuf, "Cannot allocate memory");
4786 /* Sort the atom pointer by atomic number */
4787 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
4789 mergesort(app, natoms, sizeof(Atom *), sCompareByElement);
4790 for (i = 0; i < natoms; i++) {
4791 /* ip[old_index] is new_index */
4792 ip[app[i] - mp->atoms] = i;
4794 /* Copy the atom record to atoms[] */
4795 /* The 'v' member contains crystallographic coordinates */
4796 /* The connection table and symbase are renumbered */
4797 /* Hidden flags are modified to reflect the visibility in the MainView */
4798 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4799 AtomDuplicateNoFrame(ap, app[i]);
4800 /* memmove(ap, app[i], gSizeOfAtomRecord); */
4801 MoleculeCartesianToXtal(mp, &(ap->v), &(ap->r));
4802 cp = AtomConnectData(&ap->connect);
4803 for (j = ap->connect.count - 1; j >= 0; j--) {
4806 if (SYMOP_ALIVE(ap->symop))
4807 ap->symbase = ip[ap->symbase];
4808 if (MainView_isAtomHidden(mp->mview, i)) {
4809 ap->exflags |= kAtomHiddenFlag;
4811 ap->exflags &= ~kAtomHiddenFlag;
4817 fp = fopen(fname, "wb");
4819 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4824 fprintf(fp, "Generated by Molby\n");
4827 if (mp->cell != NULL) {
4828 dp = mp->cell->cell;
4832 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]);
4834 /* Symmetry operations */
4835 if (mp->nsyms > 0) {
4836 for (i = 0; i < mp->nsyms; i++) {
4838 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]);
4841 fprintf(fp, "1 0 1 0 0 0 0 1 0 0 0 0 1\n");
4845 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4846 /* The 'v' field contains crystallographic coordinates */
4847 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);
4848 if (ap->aniso != NULL) {
4849 dp = ap->aniso->bij;
4850 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);
4852 Double temp = ap->tempFactor;
4855 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", temp, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4858 /* Special points */
4860 Vector camera, lookat, up, xvec, yvec, zvec;
4861 MainView_getCamera(mp->mview, &camera, &lookat, &up);
4862 VecSub(zvec, lookat, camera);
4863 VecCross(xvec, zvec, up);
4864 NormalizeVec(&xvec, &xvec);
4865 NormalizeVec(&yvec, &up);
4866 VecInc(xvec, lookat);
4867 VecInc(yvec, lookat);
4868 MoleculeCartesianToXtal(mp, &lookat, &lookat);
4869 MoleculeCartesianToXtal(mp, &xvec, &xvec);
4870 MoleculeCartesianToXtal(mp, &yvec, &yvec);
4871 fprintf(fp, " ORGN %9g%9g%9g 0\n", 0.0, 0.0, 0.0);
4872 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4873 fprintf(fp, " CNTR %9g%9g%9g 0\n", lookat.x, lookat.y, lookat.z);
4874 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4875 fprintf(fp, " X %9g%9g%9g 0\n", xvec.x, xvec.y, xvec.z);
4876 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4877 fprintf(fp, " Y %9g%9g%9g 0\n", yvec.x, yvec.y, yvec.z);
4878 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);
4882 fprintf(fp, " 201\n");
4883 fprintf(fp, " 205 12\n");
4884 fprintf(fp, " 301 6.6 6.6 0 0.8\n");
4885 sOutputAtomListInstructions(fp, natoms, atoms);
4886 fprintf(fp, " 501%4d55501%4d55501%4d55501%4d55501%4d55501 1\n", natoms + 2, natoms + 2, natoms + 3, natoms + 2, natoms + 4);
4887 fprintf(fp, " 502 1 0.0 2 0.0 3 0.0\n");
4888 fprintf(fp, " 604 1.538\n");
4890 sOutputBondInstructions(fp, natoms, atoms, 1);
4891 sOutputAtomTypeInstructions(fp, natoms, atoms);
4892 sOutputBondInstructions(fp, natoms, atoms, 0);
4894 for (i = 0; i < natoms; i++) {
4895 AtomClean(atoms + i);
4899 fprintf(fp, " 202\n");
4900 fprintf(fp, " 0 -1\n");
4906 MoleculeDump(Molecule *mol)
4911 for (i = 0; i < mol->natoms; i++) {
4913 ap = ATOM_AT_INDEX(mol->atoms, i);
4914 snprintf(buf1, sizeof buf1, "%3.4s.%d", ap->resName, ap->resSeq);
4915 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);
4916 cp = AtomConnectData(&ap->connect);
4917 for (j = 0; j < ap->connect.count; j++) {
4918 fprintf(stderr, "%s%d", (j > 0 ? "," : ""), cp[j]);
4920 fprintf(stderr, "]\n");
4924 #pragma mark ====== MD support (including modification of Molecule) ======
4926 /* Call md_prepare for the MDArena. If MDArena has not been created, a new arena is created.
4927 If something goes wrong, returns 1 (for missing parameters) or -1 (more serious error).
4928 If retmsg is not NULL, a message describing the problem is returned there. This message
4929 must be free'd by the caller. */
4931 MoleculePrepareMDArena(Molecule *mol, int check_only, char **retmsg)
4934 Int nangles, *angles, ndihedrals, *dihedrals, nimpropers, *impropers;
4936 IntGroup *ig1, *ig2, *ig3;
4937 MDArena *arena = mol->arena;
4939 if (arena == NULL) {
4942 } else if (arena->xmol != mol)
4943 md_arena_set_molecule(arena, mol);
4945 arena->is_initialized = 0;
4947 /* Rebuild the tables */
4948 ig1 = ig2 = ig3 = NULL;
4949 nangles = MoleculeFindMissingAngles(mol, &angles);
4950 ndihedrals = MoleculeFindMissingDihedrals(mol, &dihedrals);
4951 nimpropers = MoleculeFindMissingImpropers(mol, &impropers);
4953 ig1 = IntGroupNewWithPoints(mol->nangles, nangles, -1);
4954 MolActionCreateAndPerform(mol, gMolActionAddAngles, nangles * 3, angles, ig1);
4956 IntGroupRelease(ig1);
4958 if (ndihedrals > 0) {
4959 ig2 = IntGroupNewWithPoints(mol->ndihedrals, ndihedrals, -1);
4960 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, ndihedrals * 4, dihedrals, ig2);
4962 IntGroupRelease(ig2);
4964 if (nimpropers > 0) {
4965 ig3 = IntGroupNewWithPoints(mol->nimpropers, nimpropers, -1);
4966 MolActionCreateAndPerform(mol, gMolActionAddImpropers, nimpropers * 4, impropers, ig3);
4968 IntGroupRelease(ig3);
4972 /* Update the path information of the molecule before MD setup */
4973 char *buf = (char *)malloc(4096);
4974 MoleculeCallback_pathName(mol, buf, sizeof buf);
4975 MoleculeSetPath(mol, buf);
4979 /* Prepare parameters and internal information */
4980 msg = md_prepare(arena, check_only);
4982 /* Some parameters are missing? */
4984 if (strstr(msg, "parameter") != NULL && strstr(msg, "missing") != NULL)
4988 asprintf(retmsg, "cannot initialize for MD: %s", msg);
4993 /* The local parameter list is updated */
4996 if (mol->par == NULL)
4997 mol->par = ParameterNew();
4998 for (parType = kFirstParType; parType <= kLastParType; parType++) {
4999 /* Delete global and undefined parameters */
5000 UnionPar *up, *upbuf;
5002 ig1 = IntGroupNew();
5003 for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx)) != NULL; idx++) {
5004 if (up->bond.src != 0)
5005 IntGroupAdd(ig1, idx, 1);
5007 if (IntGroupGetCount(ig1) > 0)
5008 MolActionCreateAndPerform(mol, gMolActionDeleteParameters, parType, ig1);
5009 IntGroupRelease(ig1);
5010 /* Copy global and undefined parameters from arena and insert to mol->par */
5011 nparams = ParameterGetCountForType(arena->par, parType);
5014 upbuf = (UnionPar *)calloc(sizeof(UnionPar), nparams);
5015 ig1 = IntGroupNew();
5016 ig2 = IntGroupNew();
5017 for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(arena->par, parType, idx)) != NULL; idx++) {
5018 if (up->bond.src > 0)
5019 IntGroupAdd(ig1, idx, 1); /* Global parameter */
5020 else if (up->bond.src < 0)
5021 IntGroupAdd(ig2, idx, 1); /* Undefined parameter */
5023 if ((count = IntGroupGetCount(ig1)) > 0) {
5024 /* Insert global parameters (at the top) */
5025 ParameterCopy(arena->par, parType, upbuf, ig1);
5026 ig3 = IntGroupNewWithPoints(0, count, -1);
5027 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5028 IntGroupRelease(ig3);
5030 if ((count = IntGroupGetCount(ig2)) > 0) {
5031 /* Insert undefined parameters (at the bottom) */
5032 ParameterCopy(arena->par, parType, upbuf, ig2);
5033 idx = ParameterGetCountForType(mol->par, parType);
5034 ig3 = IntGroupNewWithPoints(idx, count, -1);
5035 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5036 IntGroupRelease(ig3);
5038 IntGroupRelease(ig2);
5039 IntGroupRelease(ig1);
5042 mol->needsMDRebuild = 0; /* We know the "modified" parameters are consistent with the MDArena */
5047 *retmsg = strdup(msg);
5052 #pragma mark ====== Serialize ======
5055 MoleculeDeserialize(const char *data, Int length, Int *timep)
5065 par = ParameterNew();
5069 while (length >= 12) {
5070 const char *ptr = data + 8 + sizeof(Int);
5071 int len = *((const Int *)(data + 8));
5073 if (strcmp(data, "ATOM") == 0) {
5074 n = len / gSizeOfAtomRecord;
5075 NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, n);
5076 memmove(mp->atoms, ptr, len);
5077 } else if (strcmp(data, "ANISO") == 0) {
5078 n = len / (sizeof(Int) + sizeof(Aniso));
5079 for (i = 0; i < n; i++) {
5080 j = *((const Int *)ptr);
5081 if (j < 0 || j >= mp->natoms)
5083 ap = ATOM_AT_INDEX(mp->atoms, j);
5084 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
5085 if (ap->aniso == NULL)
5087 *(ap->aniso) = *((Aniso *)(ptr + sizeof(Int)));
5088 ptr += sizeof(Int) + sizeof(Aniso);
5090 } else if (strcmp(data, "FRAME") == 0) {
5091 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5092 if (ap->nframes == 0)
5094 ap->frames = (Vector *)malloc(sizeof(Vector) * ap->nframes);
5095 if (ap->frames == NULL)
5097 memmove(ap->frames, ptr, sizeof(Vector) * ap->nframes);
5098 ptr += sizeof(Vector) * ap->nframes;
5100 } else if (strcmp(data, "EXTCON") == 0) {
5101 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5102 if (ap->connect.count <= ATOM_CONNECT_LIMIT)
5104 n = ap->connect.count;
5105 ap->connect.count = 0;
5106 ap->connect.u.ptr = NULL;
5107 NewArray(&(ap->connect.u.ptr), &(ap->connect.count), sizeof(Int), n);
5108 memmove(ap->connect.u.ptr, ptr, sizeof(Int) * n);
5109 ptr += sizeof(Int) * n;
5111 } else if (strcmp(data, "BOND") == 0) {
5112 n = len / (sizeof(Int) * 2);
5113 NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, n);
5114 memmove(mp->bonds, ptr, len);
5115 } else if (strcmp(data, "ANGLE") == 0) {
5116 n = len / (sizeof(Int) * 3);
5117 NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, n);
5118 memmove(mp->angles, ptr, len);
5119 } else if (strcmp(data, "DIHED") == 0) {
5120 n = len / (sizeof(Int) * 4);
5121 NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, n);
5122 memmove(mp->dihedrals, ptr, len);
5123 } else if (strcmp(data, "IMPROP") == 0) {
5124 n = len / (sizeof(Int) * 4);
5125 NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, n);
5126 memmove(mp->impropers, ptr, len);
5127 } else if (strcmp(data, "RESIDUE") == 0) {
5129 NewArray(&mp->residues, &mp->nresidues, 4, n);
5130 memmove(mp->residues, ptr, len);
5131 } else if (strcmp(data, "CELL") == 0) {
5132 mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
5133 if (mp->cell == NULL)
5135 memmove(mp->cell, ptr, sizeof(XtalCell));
5136 } else if (strcmp(data, "SYMOP") == 0) {
5137 n = len / sizeof(Transform);
5138 NewArray(&mp->syms, &mp->nsyms, sizeof(Transform), n);
5139 memmove(mp->syms, ptr, len);
5140 } else if (strcmp(data, "ANCHOR") == 0) {
5141 const char *ptr2 = ptr + len;
5142 while (ptr < ptr2) {
5144 memset(&an, 0, sizeof(an));
5146 if (i >= 0 && i < mp->natoms) {
5147 n = *((Int *)(ptr + sizeof(Int)));
5148 AtomConnectResize(&(an.connect), n);
5149 memmove(AtomConnectData(&(an.connect)), ptr + sizeof(Int) * 2, sizeof(Int) * n);
5150 NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), n);
5151 memmove(an.coeffs, ptr + sizeof(Int) * (2 + n), sizeof(Double) * n);
5152 ap = ATOM_AT_INDEX(mp->atoms, i);
5153 ap->anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
5154 memmove(ap->anchor, &an, sizeof(PiAnchor));
5156 ptr += sizeof(Int) * (2 + n) + sizeof(Double) * n;
5158 } else if (strcmp(data, "TIME") == 0) {
5160 *timep = *((Int *)ptr);
5161 } else if (strcmp(data, "BONDPAR") == 0) {
5163 n = len / sizeof(BondPar);
5164 NewArray(&par->bondPars, &par->nbondPars, sizeof(BondPar), n);
5165 memmove(par->bondPars, ptr, len);
5166 } else if (strcmp(data, "ANGPAR") == 0) {
5168 n = len / sizeof(AnglePar);
5169 NewArray(&par->anglePars, &par->nanglePars, sizeof(AnglePar), n);
5170 memmove(par->anglePars, ptr, len);
5171 } else if (strcmp(data, "DIHEPAR") == 0) {
5173 n = len / sizeof(TorsionPar);
5174 NewArray(&par->dihedralPars, &par->ndihedralPars, sizeof(TorsionPar), n);
5175 memmove(par->dihedralPars, ptr, len);
5176 } else if (strcmp(data, "IMPRPAR") == 0) {
5178 n = len / sizeof(TorsionPar);
5179 NewArray(&par->improperPars, &par->nimproperPars, sizeof(TorsionPar), n);
5180 memmove(par->improperPars, ptr, len);
5181 } else if (strcmp(data, "VDWPAR") == 0) {
5183 n = len / sizeof(VdwPar);
5184 NewArray(&par->vdwPars, &par->nvdwPars, sizeof(VdwPar), n);
5185 memmove(par->vdwPars, ptr, len);
5186 } else if (strcmp(data, "VDWPPAR") == 0) {
5188 n = len / sizeof(VdwPairPar);
5189 NewArray(&par->vdwpPars, &par->nvdwpPars, sizeof(VdwPairPar), n);
5190 memmove(par->vdwpPars, ptr, len);
5191 } else if (strcmp(data, "VCUTPAR") == 0) {
5193 n = len / sizeof(VdwCutoffPar);
5194 NewArray(&par->vdwCutoffPars, &par->nvdwCutoffPars, sizeof(VdwCutoffPar), n);
5195 memmove(par->vdwCutoffPars, ptr, len);
5197 len += 8 + sizeof(Int);
5201 if (mp->par == NULL)
5202 ParameterRelease(par);
5203 /* result = MoleculeRebuildTablesFromConnects(mp);
5209 Panic("Low memory while deserializing molecule data");
5210 return NULL; /* Not reached */
5213 Panic("internal error: bad format during deserializing molecule data");
5214 return NULL; /* Not reached */
5218 MoleculeSerialize(Molecule *mp, Int *outLength, Int *timep)
5221 int len, len_all, i, naniso, nframes, nconnects, nanchors;
5224 /* Array of atoms */
5225 len = 8 + sizeof(Int) + gSizeOfAtomRecord * mp->natoms;
5226 ptr = (char *)malloc(len);
5229 memmove(ptr, "ATOM\0\0\0\0", 8);
5230 *((Int *)(ptr + 8)) = gSizeOfAtomRecord * mp->natoms;
5231 p = ptr + 8 + sizeof(Int);
5232 memmove(p, mp->atoms, gSizeOfAtomRecord * mp->natoms);
5233 naniso = nframes = nconnects = nanchors = 0;
5234 for (i = 0; i < mp->natoms; i++) {
5235 ap = ATOM_AT_INDEX(p, i);
5236 if (ap->aniso != NULL) {
5240 if (ap->frames != NULL) {
5241 nframes += ap->nframes;
5244 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5245 nconnects += ap->connect.count;
5246 ap->connect.u.ptr = NULL;
5248 if (ap->anchor != NULL) {
5255 /* Array of aniso */
5257 len = 8 + sizeof(Int) + (sizeof(Int) + sizeof(Aniso)) * naniso;
5258 ptr = (char *)realloc(ptr, len_all + len);
5262 memmove(p, "ANISO\0\0\0", 8);
5263 *((Int *)(p + 8)) = (sizeof(Int) + sizeof(Aniso)) * naniso;
5264 p += 8 + sizeof(Int);
5265 for (i = 0; i < mp->natoms; i++) {
5266 ap = ATOM_AT_INDEX(mp->atoms, i);
5267 if (ap->aniso != NULL) {
5269 *((Aniso *)(p + sizeof(Int))) = *(ap->aniso);
5270 p += sizeof(Int) + sizeof(Aniso);
5276 /* Array of frames */
5278 len = 8 + sizeof(Int) + sizeof(Vector) * nframes;
5279 ptr = (char *)realloc(ptr, len_all + len);
5283 memmove(p, "FRAME\0\0\0", 8);
5284 *((Int *)(p + 8)) = sizeof(Vector) * nframes;
5285 p += 8 + sizeof(Int);
5286 for (i = 0; i < mp->natoms; i++) {
5287 ap = ATOM_AT_INDEX(mp->atoms, i);
5288 if (ap->frames != NULL) {
5289 memmove(p, ap->frames, sizeof(Vector) * ap->nframes);
5290 p += sizeof(Vector) * ap->nframes;
5296 /* Array of connects */
5297 if (nconnects > 0) {
5298 len = 8 + sizeof(Int) + sizeof(Int) * nconnects;
5299 ptr = (char *)realloc(ptr, len_all + len);
5303 memmove(p, "EXTCON\0\0", 8);
5304 *((Int *)(p + 8)) = sizeof(Int) * nconnects;
5305 p += 8 + sizeof(Int);
5306 for (i = 0; i < mp->natoms; i++) {
5307 ap = ATOM_AT_INDEX(mp->atoms, i);
5308 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5309 memmove(p, ap->connect.u.ptr, sizeof(Int) * ap->connect.count);
5310 p += sizeof(Int) * ap->connect.count;
5316 /* Bonds, angles, dihedrals, impropers */
5317 if (mp->nbonds > 0) {
5318 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nbonds;
5319 ptr = (char *)realloc(ptr, len_all + len);
5323 memmove(p, "BOND\0\0\0\0", 8);
5324 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nbonds;
5325 p += 8 + sizeof(Int);
5326 memmove(p, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
5329 if (mp->nangles > 0) {
5330 len = 8 + sizeof(Int) + sizeof(Int) * 3 * mp->nangles;
5331 ptr = (char *)realloc(ptr, len_all + len);
5335 memmove(p, "ANGLE\0\0\0", 8);
5336 *((Int *)(p + 8)) = sizeof(Int) * 3 * mp->nangles;
5337 p += 8 + sizeof(Int);
5338 memmove(p, mp->angles, sizeof(Int) * 3 * mp->nangles);
5341 if (mp->ndihedrals > 0) {
5342 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->ndihedrals;
5343 ptr = (char *)realloc(ptr, len_all + len);
5347 memmove(p, "DIHED\0\0\0", 8);
5348 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->ndihedrals;
5349 p += 8 + sizeof(Int);
5350 memmove(p, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
5353 if (mp->nimpropers > 0) {
5354 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->nimpropers;
5355 ptr = (char *)realloc(ptr, len_all + len);
5359 memmove(p, "IMPROP\0\0", 8);
5360 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->nimpropers;
5361 p += 8 + sizeof(Int);
5362 memmove(p, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
5366 /* Array of residues */
5367 if (mp->nresidues > 0) {
5368 len = 8 + sizeof(Int) + 4 * mp->nresidues;
5369 ptr = (char *)realloc(ptr, len_all + len);
5373 memmove(p, "RESIDUE\0", 8);
5374 *((Int *)(p + 8)) = 4 * mp->nresidues;
5375 p += 8 + sizeof(Int);
5376 memmove(p, mp->residues, 4 * mp->nresidues);
5381 if (mp->cell != NULL) {
5382 len = 8 + sizeof(Int) + sizeof(XtalCell);
5383 ptr = (char *)realloc(ptr, len_all + len);
5387 memmove(p, "CELL\0\0\0\0", 8);
5388 *((Int *)(p + 8)) = sizeof(XtalCell);
5389 p += 8 + sizeof(Int);
5390 memmove(p, mp->cell, sizeof(XtalCell));
5394 /* Symmetry operations */
5395 if (mp->nsyms > 0) {
5396 len = 8 + sizeof(Int) + sizeof(Transform) * mp->nsyms;
5397 ptr = (char *)realloc(ptr, len_all + len);
5401 memmove(p, "SYMOP\0\0\0", 8);
5402 *((Int *)(p + 8)) = sizeof(Transform) * mp->nsyms;
5403 p += 8 + sizeof(Int);
5404 memmove(p, mp->syms, sizeof(Transform) * mp->nsyms);
5410 /* Estimate the necessary storage first */
5411 /* One entry consists of { atom_index (Int), number_of_connects (Int), connects (Int's), weights (Double's) } */
5412 len = 8 + sizeof(Int);
5413 for (i = 0; i < mp->natoms; i++) {
5414 ap = ATOM_AT_INDEX(mp->atoms, i);
5415 if (ap->anchor != NULL)
5416 len += sizeof(Int) * 2 + (sizeof(Int) + sizeof(Double)) * ap->anchor->connect.count;
5418 ptr = (char *)realloc(ptr, len_all + len);
5422 memmove(p, "ANCHOR\0\0", 8);
5423 *((Int *)(p + 8)) = len - (8 + sizeof(Int));
5424 p += 8 + sizeof(Int);
5425 for (i = 0; i < mp->natoms; i++) {
5427 ap = ATOM_AT_INDEX(mp->atoms, i);
5428 if (ap->anchor != NULL) {
5429 count = ap->anchor->connect.count;
5431 *((Int *)(p + sizeof(Int))) = count;
5432 p += sizeof(Int) * 2;
5433 ip = AtomConnectData(&(ap->anchor->connect));
5434 memmove(p, ip, sizeof(Int) * count);
5435 p += sizeof(Int) * count;
5436 memmove(p, ap->anchor->coeffs, sizeof(Double) * count);
5437 p += sizeof(Double) * count;
5444 if (mp->par != NULL) {
5446 for (type = kFirstParType; type <= kLastParType; type++) {
5447 const char *parname;
5448 Int parsize, parcount;
5452 parname = "BONDPAR\0";
5453 parsize = sizeof(BondPar);
5454 parcount = mp->par->nbondPars;
5455 parptr = mp->par->bondPars;
5458 parname = "ANGPAR\0\0";
5459 parsize = sizeof(AnglePar);
5460 parcount = mp->par->nanglePars;
5461 parptr = mp->par->anglePars;
5463 case kDihedralParType:
5464 parname = "DIHEPAR\0";
5465 parsize = sizeof(TorsionPar);
5466 parcount = mp->par->ndihedralPars;
5467 parptr = mp->par->dihedralPars;
5469 case kImproperParType:
5470 parname = "IMPRPAR\0";
5471 parsize = sizeof(TorsionPar);
5472 parcount = mp->par->nimproperPars;
5473 parptr = mp->par->improperPars;
5476 parname = "VDWPAR\0\0";
5477 parsize = sizeof(VdwPar);
5478 parcount = mp->par->nvdwPars;
5479 parptr = mp->par->vdwPars;
5481 case kVdwPairParType:
5482 parname = "VDWPPAR\0";
5483 parsize = sizeof(VdwPairPar);
5484 parcount = mp->par->nvdwpPars;
5485 parptr = mp->par->vdwpPars;
5487 case kVdwCutoffParType:
5488 parname = "VCUTPAR\0";
5489 parsize = sizeof(VdwCutoffPar);
5490 parcount = mp->par->nvdwCutoffPars;
5491 parptr = mp->par->vdwCutoffPars;
5497 len = 8 + sizeof(Int) + parsize * parcount;
5498 ptr = (char *)realloc(ptr, len_all + len);
5502 memmove(p, parname, 8);
5503 *((Int *)(p + 8)) = parsize * parcount;
5504 p += 8 + sizeof(Int);
5505 memmove(p, parptr, parsize * parcount);
5513 time_t tm = time(NULL);
5514 len = 8 + sizeof(Int) + sizeof(Int);
5515 ptr = (char *)realloc(ptr, len_all + len);
5519 memmove(p, "TIME\0\0\0\0", 8);
5520 *((Int *)(p + 8)) = sizeof(Int);
5521 p += 8 + sizeof(Int);
5522 *((Int *)p) = (Int)tm;
5528 if (outLength != NULL)
5529 *outLength = len_all;
5533 Panic("Low memory while serializing a molecule data");
5534 return NULL; /* Not reached */
5537 #pragma mark ====== Search for bonds, angles, dihedrals, impropers ======
5540 sMoleculeSearchIncludingAtoms(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
5544 IntGroup *gp = NULL;
5545 if (atomgroup == NULL)
5547 for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
5548 for (j = 0; j < nsize; j++) {
5549 if (IntGroupLookup(atomgroup, ip[j], NULL) != 0) {
5552 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
5553 Panic("Low memory while searching %s", msg);
5562 MoleculeSearchBondsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5566 return sMoleculeSearchIncludingAtoms(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
5570 MoleculeSearchAnglesIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5574 return sMoleculeSearchIncludingAtoms(mp->nangles, mp->angles, 3, atomgroup, "angles");
5578 MoleculeSearchDihedralsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5582 return sMoleculeSearchIncludingAtoms(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
5586 MoleculeSearchImpropersIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5590 return sMoleculeSearchIncludingAtoms(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
5594 sMoleculeSearchAcrossAtomGroup(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
5598 IntGroup *gp = NULL;
5599 if (atomgroup == NULL)
5601 for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
5603 for (j = 0; j < nsize; j++) {
5605 kk = (IntGroupLookup(atomgroup, ip[j], NULL) != 0);
5609 /* This bond etc. crosses the atom group border */
5612 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
5613 Panic("Low memory while searching %s", msg);
5622 MoleculeSearchBondsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5626 return sMoleculeSearchAcrossAtomGroup(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
5630 MoleculeSearchAnglesAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5634 return sMoleculeSearchAcrossAtomGroup(mp->nangles, mp->angles, 3, atomgroup, "angles");
5638 MoleculeSearchDihedralsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5642 return sMoleculeSearchAcrossAtomGroup(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
5646 MoleculeSearchImpropersAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5650 return sMoleculeSearchAcrossAtomGroup(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
5653 /* Subroutine for MoleculeGuessBonds. It can be also used independently, but make sure that *outNbonds/*outBonds
5654 _correctly_ represents an array of two integers (as in mp->nbonds/mp->bonds). */
5655 /* Find atoms within the given "distance" from the given position. */
5656 /* If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
5657 the threshold distance is given by the sum of van der Waals radii times limit, and radius is
5658 the van der Waals radius of the atom at the given position. */
5659 /* Index is the atom index of the given atom; it is only used in returning the "bond" array
5660 to the caller. If index is negative, then (-index) is the real atom index, and
5661 only atoms with lower indices than (-index) are looked for. */
5663 MoleculeFindCloseAtoms(Molecule *mp, const Vector *vp, Double radius, Double limit, Int *outNbonds, Int **outBonds, Int index)
5665 Int n2, j, nlim, newbond[2];
5669 nlim = index = -index;
5673 for (j = 0; j < nlim; j++) {
5674 Atom *bp = ATOM_AT_INDEX(mp->atoms, j);
5677 n2 = bp->atomicNumber;
5678 if (n2 >= 0 && n2 < gCountElementParameters)
5679 a2 = gElementParameters[n2].radius;
5680 else a2 = gElementParameters[6].radius;
5682 VecSub(dr, *vp, r2);
5686 alim = limit * (radius + a2);
5687 if (VecLength2(dr) < alim * alim) {
5690 /* MoleculeAddBonds(mp, 1, newbonds); */
5691 AssignArray(outBonds, outNbonds, sizeof(Int) * 2, *outNbonds, newbond);
5697 /* Guess the bonds from the coordinates */
5698 /* If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
5699 the threshold distance is given by the sum of van der Waals radii times limit. */
5701 MoleculeGuessBonds(Molecule *mp, Double limit, Int *outNbonds, Int **outBonds)
5703 Int nbonds, *bonds, i, newbond[2];
5707 for (i = 1, ap = ATOM_NEXT(mp->atoms); i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5709 Int an = ap->atomicNumber;
5711 if (an >= 0 && an < gCountElementParameters)
5712 rad = gElementParameters[an].radius;
5713 else rad = gElementParameters[6].radius;
5714 MoleculeFindCloseAtoms(mp, &r, rad, limit, &nbonds, &bonds, -i);
5717 newbond[0] = kInvalidIndex;
5719 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5722 if (outNbonds != NULL)
5723 *outNbonds = nbonds;
5724 if (outBonds != NULL)
5729 /* Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information */
5731 MoleculeRebuildTablesFromConnects(Molecule *mp)
5733 int i, j, k, retval;
5740 if (mp->nbonds == 0) {
5741 for (i = 0; i < mp->natoms; i++) {
5742 ap = ATOM_AT_INDEX(mp->atoms, i);
5743 cp = AtomConnectData(&ap->connect);
5744 for (j = 0; j < ap->connect.count; j++) {
5750 /* MoleculeAddBonds() should not be used, because it assumes connects[] and
5751 bonds are already in sync */
5752 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
5753 /* retval = MoleculeAddBonds(mp, 1, ibuf);
5761 if (mp->nangles == 0) {
5762 for (i = 0; i < mp->natoms; i++) {
5763 ap = ATOM_AT_INDEX(mp->atoms, i);
5764 cp = AtomConnectData(&ap->connect);
5765 for (j = 0; j < ap->connect.count; j++) {
5766 for (k = j + 1; k < ap->connect.count; k++) {
5771 retval = MoleculeAddAngles(mp, ibuf, NULL);
5779 /* Find dihedrals */
5780 if (mp->ndihedrals == 0) {
5781 for (i = 0; i < mp->natoms; i++) {
5782 ap = ATOM_AT_INDEX(mp->atoms, i);
5783 cp = AtomConnectData(&ap->connect);
5784 for (j = 0; j < ap->connect.count; j++) {
5791 apjj = ATOM_AT_INDEX(mp->atoms, jj);
5792 cpjj = AtomConnectData(&apjj->connect);
5793 for (k = 0; k < ap->connect.count; k++) {
5797 for (m = 0; m < apjj->connect.count; m++) {
5799 if (mm == i || mm == kk)
5806 retval = MoleculeAddDihedrals(mp, ibuf, NULL);
5815 /* Find impropers */
5816 if (mp->nimpropers == 0) {
5817 for (i = 0; i < mp->natoms; i++) {
5818 int i1, i2, i4, n1, n2, n4;
5819 ap = ATOM_AT_INDEX(mp->atoms, i);
5820 cp = AtomConnectData(&ap->connect);
5821 for (i1 = 0; i1 < ap->connect.count; i1++) {
5823 for (i2 = i1 + 1; i2 < ap->connect.count; i2++) {
5825 for (i4 = i2 + 1; i4 < ap->connect.count; i4++) {
5832 retval = MoleculeAddImpropers(mp, ibuf, NULL);
5841 mp->needsMDRebuild = 1;
5842 __MoleculeUnlock(mp);
5846 __MoleculeUnlock(mp);
5851 MoleculeAreAtomsConnected(Molecule *mol, int idx1, int idx2)
5853 Atom *ap1 = ATOM_AT_INDEX(mol->atoms, idx1);
5854 if (AtomConnectHasEntry(&ap1->connect, idx2))
5856 else if (ap1->anchor != NULL && AtomConnectHasEntry(&(ap1->anchor->connect), idx2))
5861 #pragma mark ====== Atom names ======
5863 /* Look for the n1-th atom in resno-th residue (n1 is 0-based) */
5865 MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
5869 if (mp == NULL || mp->natoms == 0)
5872 for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5873 if (ap->resSeq == resno) {
5880 return lasti; /* max */
5885 MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
5889 n = strtol(s, &p, 0);
5901 if ((p = strchr(s, ':')) != NULL) {
5902 /* Residue is specified */
5904 if ((pp = strchr(s, '.')) != NULL && pp < p) {
5905 /* Residue number is also specified */
5908 *resSeq = strtol(pp + 1, &ppp, 0);
5910 return -2; /* Bad format */
5911 while (isspace(*ppp))
5914 return -2; /* Bad format */
5917 /* Check whether the "residue name" is an integer */
5918 n = strtol(s, &pp, 0);
5920 while (isspace(*pp))
5922 if (*pp == 0 || *pp == ':') {
5925 return -2; /* Bad format */
5933 if (n >= sizeof(resName))
5934 n = sizeof(resName) - 1;
5935 strncpy(resName, s, n);
5943 strncpy(atomName, p, 4);
5948 /* Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer */
5950 MoleculeAtomIndexFromString(Molecule *mp, const char *s)
5957 n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
5958 if (atomName[0] == 0) {
5959 if (n >= mp->natoms)
5960 n = -1; /* Out of range */
5963 for (n = 0; n < mp->natoms; n++) {
5964 Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
5965 if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
5966 && (resSeq < 0 || ap->resSeq == resSeq)
5967 && strncmp(atomName, ap->aname, 4) == 0) {
5971 return -1; /* Not found */
5975 MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
5979 if (mp == NULL || index < 0 || index >= mp->natoms) {
5983 ap = mp->atoms + index;
5984 if (ap->resSeq != 0) {
5985 n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
5989 snprintf(buf, bufsize, "%.4s", ap->aname);
5992 #pragma mark ====== Selection ======
5995 sMoleculeNotifyChangeSelection(Molecule *mp)
5997 /* TODO: Finer control of notification types may be necessary */
5998 MoleculeCallback_notifyModification(mp, 0);
6002 MoleculeSetSelection(Molecule *mp, IntGroup *select)
6007 IntGroupRetain(select);
6008 if (mp->selection != NULL)
6009 IntGroupRelease(mp->selection);
6010 mp->selection = select;
6011 sMoleculeNotifyChangeSelection(mp);
6015 MoleculeGetSelection(Molecule *mp)
6019 else return mp->selection;
6023 MoleculeSelectAtom(Molecule *mp, int n1, int extending)
6025 if (mp->selection == NULL)
6026 mp->selection = IntGroupNew();
6028 IntGroupClear(mp->selection);
6029 IntGroupAdd(mp->selection, n1, 1);
6030 sMoleculeNotifyChangeSelection(mp);
6034 MoleculeUnselectAtom(Molecule *mp, int n1)
6036 if (mp->selection != NULL)
6037 IntGroupRemove(mp->selection, n1, 1);
6038 sMoleculeNotifyChangeSelection(mp);
6042 MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
6044 if (mp->selection == NULL)
6045 mp->selection = IntGroupNew();
6046 IntGroupReverse(mp->selection, n1, 1);
6047 sMoleculeNotifyChangeSelection(mp);
6051 MoleculeIsAtomSelected(Molecule *mp, int n1)
6053 if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
6059 MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
6061 if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
6067 MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
6070 IntGroup *remain, *ig1, *ig2;
6072 remain = IntGroupNewFromIntGroup(remove);
6076 status = IntGroupReverse(remain, 0, mp->natoms);
6078 ig1 = IntGroupNew();
6082 status = IntGroupDifference(selection, remove, ig1);
6085 ig2 = IntGroupNew();
6089 status = IntGroupDeconvolute(ig1, remain, ig2);
6092 IntGroupRelease(remain);
6094 IntGroupRelease(ig1);
6099 IntGroupRelease(ig2);
6104 #pragma mark ====== Atom Equivalence ======
6108 struct sEqList *next;
6109 struct sEqList *link;
6112 static struct sEqList *sListBase = NULL;
6113 static struct sEqList *sListFree = NULL;
6115 static struct sEqList *
6119 if (sListFree != NULL) {
6121 sListFree = lp->next;
6122 lp->i[0] = lp->i[1] = 0;
6126 lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
6127 lp->link = sListBase;
6133 sFreeEqList(struct sEqList *list)
6135 list->next = sListFree;
6140 sDeallocateEqLists(void)
6142 struct sEqList *lp, *lp_link;
6143 for (lp = sListBase; lp != NULL; lp = lp_link) {
6152 sExistInEqList(int i, int idx, struct sEqList *list)
6154 while (list != NULL) {
6155 if (list->i[idx] == i)
6162 static struct sEqList *
6163 sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
6166 struct sEqList *list1, *list2;
6167 Int ii, jj, ni, nj, *cpi, *cpj;
6168 api = ATOM_AT_INDEX(mol->atoms, i);
6169 apj = ATOM_AT_INDEX(mol->atoms, j);
6170 if (api->atomicNumber != apj->atomicNumber)
6172 list1 = sAllocEqList();
6178 if (i == j || (db[i] != NULL && db[i] == db[j]))
6180 cpi = AtomConnectData(&api->connect);
6181 cpj = AtomConnectData(&apj->connect);
6182 for (ni = 0; ni < api->connect.count; ni++) {
6184 if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
6186 if (sExistInEqList(ii, 0, list1))
6189 for (nj = 0; nj < apj->connect.count; nj++) {
6191 if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6193 if (sExistInEqList(jj, 1, list1))
6195 list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
6199 if (list2 == NULL) {
6201 return NULL; /* No equivalent to ii */
6203 list1 = list2; /* ii is OK, try next */
6209 sDBInclude(Int *ip, int i)
6214 for (j = ip[0] - 1; j >= 0; j--) {
6222 MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
6224 Int **db; /* List of equivalents for each atom */
6226 Atom *api, *apj, *apk;
6227 Int *cpi, *cpj, *ibuf, nibuf;
6228 int i, j, k, ii, jj, kk;
6229 if (mol == NULL || mol->natoms == 0)
6231 db = (Int **)calloc(sizeof(Int *), mol->natoms);
6235 /* Find the equivalent univalent atoms */
6236 for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6237 if (api->connect.count < 2)
6239 cpi = AtomConnectData(&api->connect);
6240 for (j = 0; j < api->connect.count; j++) {
6244 if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6246 AssignArray(&ibuf, &nibuf, sizeof(Int), n, &jj);
6248 apj = ATOM_AT_INDEX(mol->atoms, jj);
6249 if (apj->connect.count != 1 || db[jj] != NULL)
6251 cpj = AtomConnectData(&apj->connect);
6252 for (k = j + 1; k < api->connect.count; k++) {
6254 if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
6256 apk = ATOM_AT_INDEX(mol->atoms, kk);
6257 if (apk->connect.count != 1 || db[kk] != NULL)
6259 if (apj->atomicNumber == apk->atomicNumber) {
6260 AssignArray(&ibuf, &nibuf, sizeof(Int), n, &kk);
6265 ip = (Int *)calloc(sizeof(Int), n + 1);
6269 memmove(ip + 1, ibuf, sizeof(Int) * n);
6270 for (k = 0; k < n; k++)
6280 /* Try matching (i,j) pair */
6281 for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6282 if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
6284 for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
6285 struct sEqList *list;
6286 if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
6288 if (api->atomicNumber != apj->atomicNumber)
6289 continue; /* Different elements do not match */
6290 if (db[i] != NULL && db[i] == db[j])
6291 continue; /* Already equivalent */
6292 list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
6294 continue; /* (i,j) do not match */
6295 while (list != NULL) {
6298 if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
6299 /* Merge db[ii] and db[jj] */
6300 k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
6301 ip = (Int *)calloc(sizeof(Int), k + 1);
6303 return NULL; /* Out of memory */
6304 if (db[ii] == NULL) {
6308 memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
6311 if (db[jj] == NULL) {
6314 memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
6323 for (k = 0; k < ip[0]; k++)
6327 printf("(%d,%d) matched: ", ii, jj);
6328 for (k = 0; k < ip[0]; k++) {
6329 printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
6339 /* Record the equivalent atoms with the lowest index for each atom */
6340 result = (Int *)calloc(sizeof(Int), mol->natoms);
6341 for (i = 0; i < mol->natoms; i++)
6343 for (i = 0; i < mol->natoms; i++) {
6344 if (result[i] >= 0 || (ip = db[i]) == NULL)
6347 for (j = 0; j < ip[0]; j++) {
6352 for (j = 0; j < ip[0]; j++) {
6353 result[ip[j + 1]] = k;
6354 db[ip[j + 1]] = NULL;
6358 sDeallocateEqLists();
6362 #pragma mark ====== Symmetry expansion ======
6365 MoleculeGetTransformForSymop(Molecule *mp, Symop symop, Transform *tf, int is_cartesian)
6368 if (mp == NULL || mp->cell == NULL)
6370 if (symop.sym >= mp->nsyms && symop.sym != 0)
6372 memmove(*tf, SYMMETRY_AT_INDEX(mp->syms, symop.sym), sizeof(Transform));
6373 (*tf)[9] += symop.dx;
6374 (*tf)[10] += symop.dy;
6375 (*tf)[11] += symop.dz;
6377 TransformMul(t, *tf, mp->cell->rtr);
6378 TransformMul(*tf, mp->cell->tr, t);
6384 MoleculeGetSymopForTransform(Molecule *mp, const Transform tf, Symop *symop, int is_cartesian)
6388 if (mp == NULL || mp->cell == NULL)
6391 TransformMul(t, tf, mp->cell->tr);
6392 TransformMul(t, mp->cell->rtr, t);
6394 memmove(t, tf, sizeof(Transform));
6396 for (i = 0; i < mp->nsyms || i == 0; i++) {
6397 Transform *tp = &(SYMMETRY_AT_INDEX(mp->syms, i));
6398 for (j = 0; j < 9; j++) {
6399 if (fabs((*tp)[j] - t[j]) > 1e-4)
6403 for (j = 9; j < 12; j++) {
6404 double f1 = t[j] - (*tp)[j];
6405 double f2 = floor(f1 + 0.5);
6406 if (fabs(f1 - f2) > 1e-4)
6416 symop->alive = (SYMOP_ALIVE((*symop)) != 0);
6421 return -3; /* Not found */
6425 MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
6429 if (symop.sym >= mp->nsyms && symop.sym != 0)
6431 if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
6432 TransformVec(vpout, mp->cell->rtr, vpin);
6433 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpout);
6434 vpout->x += symop.dx;
6435 vpout->y += symop.dy;
6436 vpout->z += symop.dz;
6437 TransformVec(vpout, mp->cell->tr, vpout);
6439 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpin);
6440 vpout->x += symop.dx;
6441 vpout->y += symop.dy;
6442 vpout->z += symop.dz;
6447 /* Add expanded atoms. Returns the number of newly created atoms.
6448 If indices is non-NULL, it should be an array of Int with at least
6449 IntGroupGetCount(group) entries, and on return it contains the
6450 indices of the expanded atoms (may be existing atoms if the expanded
6451 atoms are already present)
6452 If allowOverlap is non-zero, then the new atom is created even when the
6453 coordinates coincide with the some other atom (special position) of the
6454 same element; otherwise, such atom will not be created and the existing
6455 atom is returned in indices[]. */
6457 MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group, Int *indices, Int allowOverlap)
6459 int i, n, n0, n1, n2, base, count, *table;
6461 IntGroupIterator iter;
6467 if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
6469 if (symop.sym != 0 && symop.sym >= mp->nsyms)
6472 /* Create atoms, with avoiding duplicates */
6473 n0 = n1 = mp->natoms;
6474 table = (int *)malloc(sizeof(int) * n0);
6477 for (i = 0; i < n0; i++)
6479 IntGroupIteratorInit(group, &iter);
6480 MoleculeGetTransformForSymop(mp, symop, &tr, 0);
6482 for (i = 0; i < count; i++) {
6483 n = IntGroupIteratorNext(&iter);
6484 ap = ATOM_AT_INDEX(mp->atoms, n);
6485 if (SYMOP_ALIVE(ap->symop)) {
6486 /* Calculate the cumulative symop */
6488 MoleculeGetTransformForSymop(mp, ap->symop, &t1, 0);
6489 TransformMul(tr2, tr, t1);
6490 if (MoleculeGetSymopForTransform(mp, tr2, &symop1, 0) != 0) {
6491 if (indices != NULL)
6493 continue; /* Skip this atom */
6501 /* Calculate the expande position */
6502 MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
6504 /* Is this expansion already present? */
6505 for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
6506 /* Symmetry operation and the base atom are the same */
6507 if (ap2->symbase == base && SYMOP_EQUAL(symop1, ap2->symop))
6509 /* Atomic number and the position are the same */
6510 if (ap2->atomicNumber == ap->atomicNumber && allowOverlap == 0) {
6511 VecSub(dr, ap2->r, nr);
6512 if (VecLength2(dr) < 1e-6)
6517 /* If yes, then skip it */
6518 if (indices != NULL)
6522 /* Create a new atom */
6524 AtomDuplicate(&newAtom, ap);
6525 MoleculeCreateAnAtom(mp, &newAtom, -1);
6526 AtomClean(&newAtom);
6527 ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
6529 ap2->symbase = base;
6530 ap2->symop = symop1;
6531 ap2->symop.alive = (symop1.dx != 0 || symop1.dy != 0 || symop1.dz != 0 || symop1.sym != 0);
6532 table[n] = n1; /* The index of the new atom */
6533 MoleculeSetAnisoBySymop(mp, n1); /* Recalculate anisotropic parameters according to symop */
6534 if (indices != NULL)
6539 IntGroupIteratorRelease(&iter);
6542 for (i = n0; i < n1; i++) {
6544 ap = ATOM_AT_INDEX(mp->atoms, i);
6545 if (SYMOP_ALIVE(ap->symop) && MoleculeGetTransformForSymop(mp, ap->symop, &tr, 1) == 0) {
6546 /* For each connected atom, look for the transformed atom */
6548 ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
6549 cp = AtomConnectData(&ap2->connect);
6550 n2 = ap2->connect.count;
6551 for (n = 0; n < n2; n++) {
6552 Atom *apn = ATOM_AT_INDEX(mp->atoms, cp[n]);
6554 TransformVec(&nr, tr, &nr);
6555 /* Look for the bonded atom transformed by ap->symop */
6556 for (j = 0, ap2 = mp->atoms; j < mp->natoms; j++, ap2 = ATOM_NEXT(ap2)) {
6557 if (ap2->symbase == cp[n] && SYMOP_EQUAL(ap->symop, ap2->symop))
6559 VecSub(dr, nr, ap2->r);
6560 if (ap2->atomicNumber == apn->atomicNumber && VecLength2(dr) < 1e-6)
6563 if (j < mp->natoms) {
6564 /* Bond i-j is created */
6567 if (MoleculeLookupBond(mp, b[0], b[1]) < 0)
6568 MoleculeAddBonds(mp, 1, b, NULL, 1);
6573 mp->needsMDRebuild = 1;
6574 __MoleculeUnlock(mp);
6576 return n1 - n0; /* The number of added atoms */
6579 /* Recalculate the coordinates of symmetry expanded atoms.
6580 (Also recalculate the positions of pi-anchor atoms)
6581 Returns the number of affected atoms.
6582 If group is non-NULL, only the expanded atoms whose base atoms are in the
6583 given group are considered.
6584 If groupout and vpout are non-NULL, the indices of the affected atoms
6585 and the original positions are returned (for undo operation).
6586 The pointers returned in *groupout and *vpout must be released and
6587 free()'ed by the caller */
6589 MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
6594 IntGroup *ig = NULL;
6597 if (mp == NULL || mp->natoms == 0)
6602 if (mp->nsyms != 0) {
6603 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6604 if (!SYMOP_ALIVE(ap->symop))
6606 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
6608 bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
6609 MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
6610 VecSub(dr, nr, ap->r);
6611 if (VecLength2(dr) < 1e-20)
6613 if (groupout != NULL) {
6616 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
6619 IntGroupAdd(ig, i, 1);
6625 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6627 if (ap->anchor == NULL)
6629 if (group != NULL) {
6630 if (IntGroupLookup(group, i, NULL) == 0) {
6631 n = ap->anchor->connect.count;
6632 ip = AtomConnectData(&(ap->anchor->connect));
6633 for (j = 0; j < n; j++) {
6634 if (IntGroupLookup(group, ip[j], NULL) != 0)
6638 continue; /* This pi-anchor should not be modified */
6642 MoleculeCalculatePiAnchorPosition(mp, i);
6643 VecSub(dr, nr, ap->r);
6644 if (VecLength2(dr) < 1e-20) {
6645 ap->r = nr; /* No change */
6648 if (groupout != NULL) {
6651 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
6654 IntGroupAdd(ig, i, 1);
6658 mp->needsMDCopyCoordinates = 1;
6659 __MoleculeUnlock(mp);
6662 if (groupout != NULL && vpout != NULL) {
6664 *vpout = (Vector *)realloc(vp, sizeof(Vector) * count);
6666 IntGroupRelease(ig);
6670 if (groupout != NULL && vpout != NULL) {
6678 #pragma mark ====== Show/hide atoms ======
6681 sMoleculeNotifyChangeAppearance(Molecule *mp)
6683 /* TODO: Finer control of notification types may be necessary */
6684 MoleculeCallback_notifyModification(mp, 0);
6689 sMoleculeUnselectHiddenAtoms(Molecule *mp)
6692 if (mp == NULL || mp->selection == NULL)
6694 for (i = 0; i < mp->natoms; i++) {
6695 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6696 if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
6697 IntGroupRemove(mp->selection, i, 1);
6699 sMoleculeNotifyChangeAppearance(mp);
6703 MoleculeShowAllAtoms(Molecule *mp)
6708 for (i = 0; i < mp->natoms; i++) {
6709 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6710 ap->exflags &= ~kAtomHiddenFlag;
6712 sMoleculeNotifyChangeAppearance(mp);
6717 MoleculeShowReverse(Molecule *mp)
6722 for (i = 0; i < mp->natoms; i++) {
6723 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6724 ap->exflags ^= kAtomHiddenFlag;
6726 sMoleculeUnselectHiddenAtoms(mp);
6727 sMoleculeNotifyChangeAppearance(mp);
6732 MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
6735 if (mp == NULL || ig == NULL)
6737 for (i = 0; i < mp->natoms; i++) {
6738 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6739 if (ap->exflags & kAtomHiddenFlag)
6740 continue; /* Already hidden */
6741 if (IntGroupLookupPoint(ig, i) >= 0)
6742 ap->exflags |= kAtomHiddenFlag;
6744 sMoleculeUnselectHiddenAtoms(mp);
6745 sMoleculeNotifyChangeAppearance(mp);
6749 #pragma mark ====== Reversible Editing ======
6753 sMoleculeNotifyModification(Molecule *mp)
6755 ** TODO: Finer control of notification types may be necessary **
6756 MoleculeCallback_notifyModification(mp, 0);
6760 /* Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup */
6762 sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
6765 if (where == NULL) {
6766 /* Append the new objects at the end */
6767 memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
6770 n1 = IntGroupGetCount(where); /* Position to get new object */
6771 n2 = nobjs; /* Position to get old object */
6772 n3 = n1 + n2; /* Position to place new/old object */
6773 for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
6774 int start = IntGroupGetStartPoint(where, i);
6775 int end = IntGroupGetEndPoint(where, i);
6777 /* old[end-(n3-n2)..n2-1] is moved to old[end..n3-1] */
6778 memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
6779 n2 = end - (n3 - n2);
6782 /* new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1] */
6783 memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
6790 /* Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup */
6792 sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6794 int n1, n2, n3, start, end, i;
6795 if (where == NULL || IntGroupGetCount(where) == 0)
6796 return 0; /* No operation */
6797 if (objs == NULL || nobjs == 0)
6798 return 1; /* Bad argument */
6799 n1 = 0; /* Position to move remaining elements to */
6800 n2 = 0; /* Position to move remaining elements from */
6801 n3 = 0; /* Position to move removed elements to */
6802 for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6803 end = IntGroupGetEndPoint(where, i);
6805 /* Move (start - n2) elements from objs[n2] to objs[n1] */
6807 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
6811 /* Move (end - start) elements from objs[n2] to clip[n3] */
6813 memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
6814 n3 += (end - start);
6815 n2 += (end - start);
6817 /* Move (nobjs - n2) elements from objs[n2] to objs[n1] */
6819 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
6823 /* Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup */
6825 sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6827 int n1, start, end, i;
6828 if (objs == NULL || where == NULL)
6829 return 1; /* Bad argument */
6830 n1 = 0; /* Position to move removed elements to */
6831 for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6832 end = IntGroupGetEndPoint(where, i);
6833 /* Copy (end - start) elements from objs[start] to clip[n1] */
6835 memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
6836 n1 += (end - start);
6841 /* Create a new atom with no bonding information. ap must _not_ be inside the given molecule
6842 (Use AtomDuplicate() first) */
6844 MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
6848 if (mp == NULL || ap == NULL || mp->noModifyTopology)
6851 if (pos < 0 || pos >= mp->natoms)
6853 ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
6855 goto error; /* Out of memory */
6856 ap1 = ATOM_AT_INDEX(mp->atoms, pos);
6857 if (pos < mp->natoms - 1) {
6858 memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6860 if (AtomDuplicate(ap1, ap) == NULL) {
6861 /* Cannot duplicate: restore the original state */
6862 memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6866 ap1->connect.count = 0;
6867 if (ap1->resSeq >= mp->nresidues)
6868 AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
6869 if (ap1->resName[0] == 0)
6870 strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
6871 if (ap1->segName[0] == 0)
6872 strncpy(ap1->segName, "MAIN", 4);
6873 if (pos < mp->natoms - 1) {
6874 /* Renumber the connect table, bonds, angles, etc. */
6875 for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
6878 cp = AtomConnectData(&api->connect);
6879 for (j = 0; j < api->connect.count; j++) {
6883 if (api->anchor != NULL) {
6884 cp = AtomConnectData(&api->anchor->connect);
6885 for (j = 0; j < api->anchor->connect.count; j++) {
6891 for (i = 0; i < mp->nbonds * 2; i++) {
6892 if (mp->bonds[i] >= pos)
6895 for (i = 0; i < mp->nangles * 3; i++) {
6896 if (mp->angles[i] >= pos)
6899 for (i = 0; i < mp->ndihedrals * 4; i++) {
6900 if (mp->dihedrals[i] >= pos)
6903 for (i = 0; i < mp->nimpropers * 4; i++) {
6904 if (mp->impropers[i] >= pos)
6908 mp->nframes = -1; /* Should be recalculated later */
6909 MoleculeIncrementModifyCount(mp);
6910 mp->needsMDRebuild = 1;
6911 __MoleculeUnlock(mp);
6914 __MoleculeUnlock(mp);
6920 static int s_error_count;
6923 s_fprintf(FILE *fp, const char *fmt, ...)
6928 return vfprintf(fp, fmt, va);
6932 MoleculeCheckSanity(Molecule *mol)
6934 const char *fail = "Sanity check failure";
6935 Int i, j, *ip, c[4];
6938 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
6939 if (ap->resSeq >= mol->nresidues)
6940 s_fprintf(stderr, "%s: atom %d residue %d but nresidues %d\n", fail, i, ap->resSeq, mol->nresidues);
6941 if (ap->type != 0 && ap->type < kAtomTypeMinimum)
6942 s_fprintf(stderr, "%s: atom %d atom type %d less than minimum\n", fail, i, ap->type);
6943 if (ap->atomicNumber < 0 || ap->atomicNumber > 113)
6944 s_fprintf(stderr, "%s: atom %d atomic number %d\n", fail, i, ap->atomicNumber);
6945 ip = AtomConnectData(&ap->connect);
6946 for (j = 0; j < ap->connect.count; j++) {
6947 if (ip[j] < 0 || ip[j] >= mol->natoms)
6948 s_fprintf(stderr, "%s: atom %d connect[%d] = %d out of range\n", fail, i, j, ip[j]);
6949 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[j])->connect), i) == 0)
6950 s_fprintf(stderr, "%s: atom %d has connect %d but atom %d has no connect %d\n", fail, i, ip[j], ip[j], i);
6953 for (i = 0, ip = mol->bonds; i < mol->nbonds; i++, ip += 2) {
6954 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms)
6955 s_fprintf(stderr, "%s: bond %d %d-%d out of range\n", fail, i, ip[0], ip[1]);
6956 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[0])->connect), ip[1]) == 0)
6957 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]);
6959 for (i = 0, ip = mol->angles; i < mol->nangles; i++, ip += 3) {
6960 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms || ip[2] < 0 || ip[2] >= mol->natoms)
6961 s_fprintf(stderr, "%s: angle %d %d-%d-%d out of range\n", fail, i, ip[0], ip[1], ip[2]);
6962 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
6964 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]);
6965 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
6967 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]);
6968 if (c[0] == 2 && c[1] == 2)
6969 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]);
6971 for (i = 0, ip = mol->dihedrals; i < mol->ndihedrals; i++, ip += 4) {
6972 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)
6973 s_fprintf(stderr, "%s: dihedral %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
6974 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
6975 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
6976 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
6978 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]);
6980 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]);
6982 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]);
6984 for (i = 0, ip = mol->impropers; i < mol->nimpropers; i++, ip += 4) {
6985 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)
6986 s_fprintf(stderr, "%s: improper %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
6987 c[0] = MoleculeAreAtomsConnected(mol, ip[2], ip[0]);
6988 c[1] = MoleculeAreAtomsConnected(mol, ip[2], ip[1]);
6989 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
6991 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]);
6993 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]);
6995 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]);
6997 return s_error_count;
7001 /* Merge two molecules. We use this procedure for all add-atom operations. */
7002 /* resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
7003 /* If nactions and actions are non-NULL, then the corresponding undo actions are created and returned. */
7004 /* If forUndo is non-zero, then only the atoms are inserted; other information should be inserted
7005 separately by other undo actions. */
7007 MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, Int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
7010 Int i, j, n1, n2, n3, n4, *cp;
7011 Int *new2old, *old2new;
7016 if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
7017 return 0; /* Do nothing */
7019 if (dst->noModifyTopology)
7020 return 1; /* Prohibited operation */
7022 if (where != NULL && IntGroupGetCount(where) != src->natoms)
7023 return 1; /* Bad parameter */
7025 if (nactions != NULL)
7027 if (actions != NULL)
7031 __MoleculeLock(dst);
7035 if (resSeqOffset < 0)
7038 /* Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
7039 and ndst..ndst+nsrc-1 are for atoms in src. */
7040 new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
7041 if (new2old == NULL)
7043 old2new = new2old + ndst + nsrc;
7044 n1 = 0; /* dst index */
7045 n2 = 0; /* src index */
7046 n3 = 0; /* "merged" index */
7048 while (n1 < ndst || n2 < nsrc) {
7049 if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
7052 /* n4 elements from dst[n1] will go to merged[n3] */
7053 for (j = 0; j < n4; j++) {
7054 old2new[n1 + j] = n3 + j;
7055 new2old[n3 + j] = n1 + j;
7059 if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
7061 /* n4 elements from src[n2] will go to merged[n3] */
7062 for (j = 0; j < n4; j++) {
7063 old2new[ndst + n2 + j] = n3 + j;
7064 new2old[n3 + j] = ndst + n2 + j;
7071 /* Expand the destination array */
7072 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
7075 /* Move the atoms */
7076 if (where == NULL) {
7077 /* Duplicate atoms to the end of the destination array */
7078 for (i = 0; i < nsrc; i++) {
7079 ap = ATOM_AT_INDEX(dst->atoms, ndst + i);
7080 if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7082 if (forUndo) /* For undo action, all bonds come from another undo action, so connection info are cleared */
7083 AtomConnectResize(&ap->connect, 0);
7086 /* Duplicate to a temporary storage and then insert */
7087 Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
7088 if (tempatoms == NULL)
7090 for (i = 0; i < nsrc; i++) {
7091 ap = ATOM_AT_INDEX(tempatoms, i);
7092 if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7094 if (forUndo) /* See above */
7095 AtomConnectResize(&ap->connect, 0);
7097 if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
7101 dst->natoms = ndst + nsrc;
7103 /* Renumber the atom indices in connect[] and symbase, and modify the residue numbers */
7104 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7105 if (new2old[i] < ndst) {
7106 /* This atom is from dst */
7109 /* This atom is from src */
7110 n1 = ndst; /* Offset to the internal number */
7111 if (ap->resSeq != 0)
7112 ap->resSeq += resSeqOffset; /* Modify residue number */
7114 cp = AtomConnectData(&ap->connect);
7115 for (j = 0; j < ap->connect.count; j++)
7116 cp[j] = old2new[cp[j] + n1];
7117 if (SYMOP_ALIVE(ap->symop))
7118 ap->symbase = old2new[ap->symbase + n1];
7119 if (ap->anchor != NULL) {
7120 cp = AtomConnectData(&ap->anchor->connect);
7121 for (j = 0; j < ap->anchor->connect.count; j++)
7122 cp[j] = old2new[cp[j] + n1];
7126 /* Move the bonds, angles, dihedrals, impropers */
7127 for (i = 0; i < 4; i++) {
7128 Int *nitems, *nitems_src;
7129 Int **items, **items_src;
7130 Int nsize; /* Number of Ints in one element */
7133 nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
7135 nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
7137 nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
7139 nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
7141 nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
7142 items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
7144 /* During undo, no bonds etc. are copied from src; they will be taken care later
7149 /* Keep the old number of entries in dst, because it is updated by AssignArray() */
7151 /* Also keep the old number of entries in src, in case src and dst point the same molecule */
7153 /* Expand the array */
7154 if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
7156 /* Copy the items */
7157 memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
7159 /* Copy the bond order info if present */
7160 Int nn1 = dst->nbondOrders;
7161 if (dst->bondOrders != NULL || src->bondOrders != NULL) {
7162 if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), dst->nbonds - 1, NULL) == NULL)
7164 memset(dst->bondOrders + nn1, 0, sizeof(Double) * (dst->nbonds - nn1));
7165 if (src->bondOrders != NULL)
7166 memmove(dst->bondOrders + n1, src->bondOrders, sizeof(Double) * n2);
7171 for (j = 0; j < n1 * nsize; j++)
7172 (*items)[j] = old2new[(*items)[j]];
7173 for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
7174 (*items)[j] = old2new[(*items)[j] + ndst];
7175 if (forUndo == 0 && actions != NULL) {
7176 ig = IntGroupNewWithPoints(n1, n2, -1);
7178 case 0: act = MolActionNew(gMolActionDeleteBonds, ig); break;
7179 case 1: act = MolActionNew(gMolActionDeleteAngles, ig); break;
7180 case 2: act = MolActionNew(gMolActionDeleteDihedrals, ig); break;
7181 case 3: act = MolActionNew(gMolActionDeleteImpropers, ig); break;
7183 IntGroupRelease(ig);
7184 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7189 /* Renumber existing parameters */
7190 if (dst->par != NULL) {
7192 for (type = kFirstParType; type <= kLastParType; type++) {
7194 n1 = ParameterGetCountForType(dst->par, type);
7195 for (i = 0; i < n1; i++) {
7196 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7197 ParameterRenumberAtoms(type, up1, ndst, old2new);
7202 /* Merge parameters from src */
7203 if (src->par != NULL && forUndo == 0) {
7204 UnionPar *up1, *up2;
7206 if (dst->par == NULL)
7207 dst->par = ParameterNew();
7209 /* Renumber existing parameters */
7210 for (type = kFirstParType; type <= kLastParType; type++) {
7211 n1 = ParameterGetCountForType(dst->par, type);
7212 for (i = 0; i < n1; i++) {
7213 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7214 ParameterRenumberAtoms(type, up1, ndst, old2new);
7219 for (type = kFirstParType; type <= kLastParType; type++) {
7220 n1 = ParameterGetCountForType(src->par, type);
7221 n2 = ParameterGetCountForType(dst->par, type);
7224 /* Determine which parameter should be copied from src to dst */
7225 for (i = 0; i < n1; i++) {
7227 up1 = ParameterGetUnionParFromTypeAndIndex(src->par, type, i);
7228 n3 = ParameterGetAtomTypes(type, up1, types);
7229 for (j = 0; j < n3; j++) {
7230 /* If it includes explicit atom index, then it should be copied */
7231 if (types[j] < kAtomTypeMinimum) {
7232 IntGroupAdd(ig, i, 1);
7237 for (j = 0; j < n2; j++) {
7238 up2 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, j);
7239 if (ParameterCompare(up1, up2, type))
7243 /* This is an unknown parameter; should be copied */
7244 IntGroupAdd(ig, i, 1);
7247 n1 = IntGroupGetCount(ig);
7250 up1 = (UnionPar *)calloc(sizeof(UnionPar), n1);
7253 /* Copy parameters and renumber indices if necessary */
7254 for (i = j = 0; i < n1; i++) {
7255 up2 = ParameterGetUnionParFromTypeAndIndex(src->par, type, IntGroupGetNthPoint(ig, i));
7259 ParameterRenumberAtoms(type, up1 + j, nsrc, old2new + ndst);
7262 /* Merge parameters */
7264 IntGroupAdd(ig, n2, j);
7265 if (ParameterInsert(dst->par, type, up1, ig) < j)
7267 if (actions != NULL) {
7268 act = MolActionNew(gMolActionDeleteParameters, type, ig);
7269 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7275 IntGroupRelease(ig);
7278 /* Copy the residues if necessary */
7279 /* src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
7280 However, 1+resSeqOffset should not overwrite the existing residue in dst;
7281 i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1]. */
7283 n1 = dst->nresidues;
7284 if (1 + resSeqOffset < n1) {
7286 } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
7287 if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
7288 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
7290 memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
7291 if (nactions != NULL) {
7292 act = MolActionNew(gMolActionChangeNumberOfResidues, n1);
7293 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7299 MoleculeCleanUpResidueTable(dst);
7302 dst->nframes = -1; /* Should be recalculated later */
7304 MoleculeIncrementModifyCount(dst);
7305 dst->needsMDRebuild = 1;
7306 __MoleculeUnlock(dst);
7310 __MoleculeUnlock(dst);
7311 Panic("Low memory while adding atoms");
7312 return 1; /* Not reached */
7315 /* Unmerge the molecule. If necessary, the undo actions are stored in nactions/actions array.
7316 (The nactions/actions array must be initialized by the caller) */
7318 sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag, Int *nactions, MolAction ***actions, Int forUndo)
7320 Int nsrc, ndst, nsrcnew;
7321 Int i, j, n1, n2, n3, n4, *cp;
7322 Int *new2old, *old2new;
7323 IntGroup *move_g, *del_g, *remain_g, *dst_par_g, *remove_par_g;
7329 if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
7336 if (src->noModifyTopology && moveFlag)
7337 return 1; /* Prohibit editing */
7339 if ((ndst = IntGroupGetCount(where)) > src->natoms)
7340 return 1; /* Bad parameter */
7342 __MoleculeLock(src);
7347 nsrcnew = nsrc - ndst;
7348 if (resSeqOffset < 0)
7351 /* Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
7352 and nsrcnew..nsrc-1 are for atoms moved into dst. */
7353 new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
7354 if (new2old == NULL)
7356 old2new = new2old + nsrc;
7357 n1 = 0; /* src index */
7358 n2 = 0; /* dst index */
7359 n3 = 0; /* src index after "unmerge" */
7361 while (n1 < nsrc || n2 < ndst) {
7362 if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
7365 /* n4 elements from src[n1] will go to unmerged[n3] */
7366 for (j = 0; j < n4; j++) {
7367 old2new[n1 + j] = n3 + j;
7368 new2old[n3 + j] = n1 + j;
7372 if ((n4 = IntGroupGetInterval(where, i)) < 0)
7374 /* n4 elements from src[n1] will go to dst[n2] */
7375 for (j = 0; j < n4; j++) {
7376 old2new[n1 + j] = nsrcnew + n2 + j;
7377 new2old[nsrcnew + n2 + j] = n1 + j;
7384 /* Atoms to remain in the source group */
7386 remain_g = IntGroupNewWithPoints(0, nsrc, -1);
7387 IntGroupRemoveIntGroup(remain_g, where);
7388 } else remain_g = NULL;
7390 /* Find parameters to be moved to the dst (dst_par_g), and to be removed from the src (remove_par_g) */
7391 if (src->par != NULL) {
7392 dst_par_g = IntGroupNew();
7394 remove_par_g = IntGroupNew();
7395 else remove_par_g = NULL;
7396 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7397 n2 = ParameterGetCountForType(src->par, n1);
7400 for (i = 0; i < n2; i++) {
7401 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7402 if (ParameterIsRelevantToAtomGroup(n1, up, src->atoms, where)) {
7403 /* This parameter is to be copied to dst */
7404 IntGroupAdd(dst_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7406 if (moveFlag && !ParameterIsRelevantToAtomGroup(n1, up, src->atoms, remain_g)) {
7407 /* This parameter is to be removed */
7408 IntGroupAdd(remove_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7412 } else dst_par_g = remove_par_g = NULL;
7414 /* Pi anchors should be modified if the anchor and its component atoms become separated between
7417 Int ibufsize, *ibuf, flag_i, flag_j;
7419 ibuf = (Int *)malloc(sizeof(Int) * ibufsize);
7420 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
7421 if (ap->anchor == NULL)
7423 flag_i = (old2new[i] < nsrcnew);
7424 cp = AtomConnectData(&ap->anchor->connect);
7425 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7426 flag_j = (old2new[cp[j]] < nsrcnew);
7427 if (flag_i == flag_j) {
7428 if (n1 >= ibufsize) {
7430 ibuf = (Int *)realloc(ibuf, sizeof(Int) * ibufsize);
7436 /* Need to modify the pi anchor list */
7439 MolActionCreateAndPerform(src, SCRIPT_ACTION("isI"), "set_atom_attr", i, "anchor_list", n1, ibuf);
7444 /* Make a new molecule */
7446 dst = MoleculeNew();
7449 /* Expand the destination array */
7450 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
7452 dst_ap = dst->atoms;
7455 dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
7460 /* Move the atoms */
7462 if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
7464 src->natoms = nsrcnew;
7466 /* The atom record must be deallocated correctly */
7467 for (i = 0; i < ndst; i++)
7468 AtomClean(ATOM_AT_INDEX(dst_ap, i));
7472 for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
7473 AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
7478 /* The dummy destination array is no longer needed */
7483 /* Renumber the atom indices in connect[] (src) */
7485 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
7486 cp = AtomConnectData(&ap->connect);
7487 for (j = n1 = 0; j < ap->connect.count; j++) {
7488 n2 = old2new[cp[j]];
7492 AtomConnectResize(&ap->connect, n1);
7493 if (ap->anchor != NULL) {
7494 cp = AtomConnectData(&ap->anchor->connect);
7495 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7496 n2 = old2new[cp[j]];
7500 if (n1 != ap->anchor->connect.count) {
7501 /* This should not happen!! */
7502 AtomConnectResize(&ap->anchor->connect, n1);
7503 fprintf(stderr, "Internal error in sMoleculeUnmergeSub (line %d)\n", __LINE__);
7505 free(ap->anchor->coeffs);
7514 /* Renumber the atom indices in connect[] (dst) */
7516 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7517 if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
7518 ap->resSeq -= resSeqOffset;
7519 else ap->resSeq = 0;
7520 cp = AtomConnectData(&ap->connect);
7521 for (j = n1 = 0; j < ap->connect.count; j++) {
7522 n2 = old2new[cp[j]] - nsrcnew;
7526 AtomConnectResize(&ap->connect, n1);
7527 if (ap->anchor != NULL) {
7528 cp = AtomConnectData(&ap->anchor->connect);
7529 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7530 n2 = old2new[cp[j]] - nsrcnew;
7534 if (n1 != ap->anchor->connect.count) {
7535 /* This can happen, and the anchor info is silently modified */
7537 AtomConnectResize(&ap->anchor->connect, 0);
7538 free(ap->anchor->coeffs);
7543 AtomConnectResize(&ap->anchor->connect, n1);
7545 for (j = 0; j < n1; j++)
7546 d += ap->anchor->coeffs[j];
7547 for (j = 0; j < n1; j++)
7548 ap->anchor->coeffs[j] /= d;
7549 MoleculeCalculatePiAnchorPosition(dst, i);
7556 /* Separate the bonds, angles, dihedrals, impropers */
7557 /* TODO: Improper torsions should also be copied! */
7558 move_g = IntGroupNew();
7561 for (i = 3; i >= 0; i--) {
7562 Int *nitems, *nitems_dst;
7563 Int **items, **items_dst;
7564 Int nsize; /* Number of Ints in one element */
7565 unsigned char *counts;
7566 del_g = IntGroupNew();
7569 nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
7571 nitems = &src->nangles; items = &src->angles; nsize = 3; break;
7573 nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
7575 nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
7577 nitems = NULL; items = NULL; nsize = 0; break; /* Not reached */
7580 nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
7581 items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
7586 counts = (unsigned char *)calloc(1, *nitems);
7587 /* Find the entries that should be moved to dst */
7589 for (j = 0; j < *nitems * nsize; j++) {
7590 n1 = old2new[(*items)[j]];
7592 counts[j / nsize]++; /* Count the atom belonging to dst */
7594 for (j = n2 = n3 = 0; j < *nitems; j++) {
7595 if (counts[j] > 0) {
7596 /* Remove from src */
7598 if (IntGroupAdd(del_g, j, 1) != 0)
7600 if (counts[j] == nsize) {
7603 if (IntGroupAdd(move_g, j, 1) != 0)
7609 /* Expand the destination array */
7610 if (items_dst != NULL && n3 > 0) {
7611 if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
7613 if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
7615 if (i == 0 && src->bondOrders != NULL) {
7616 if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), n3 - 1, NULL) == NULL)
7618 if (sCopyElementsFromArrayAtPositions(src->bondOrders, src->nbondOrders, dst->bondOrders, sizeof(Double), move_g) != 0)
7622 /* Remove from src */
7623 if (moveFlag && forUndo == 0) {
7624 if (nactions != NULL) {
7627 ip = (Int *)malloc(sizeof(Int) * nsize * n2);
7628 for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
7629 memmove(ip + j * nsize, *items + k * nsize, sizeof(Int) * nsize);
7630 if (i == 0 && src->bondOrders != NULL) {
7631 dp = (Double *)malloc(sizeof(Double) * n2);
7632 for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
7633 dp[j] = src->bondOrders[k];
7637 act = MolActionNew(gMolActionAddBondsForUndo, n2 * nsize, ip, del_g); break;
7639 act = MolActionNew(gMolActionAddAngles, n2 * nsize, ip, del_g); break;
7641 act = MolActionNew(gMolActionAddDihedrals, n2 * nsize, ip, del_g); break;
7643 act = MolActionNew(gMolActionAddImpropers, n2 * nsize, ip, del_g); break;
7646 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7651 act = MolActionNew(gMolActionAssignBondOrders, n2, dp, del_g);
7652 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7657 if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
7662 /* Renumber the entries */
7664 for (j = 0; j < *nitems * nsize; j++) {
7665 (*items)[j] = old2new[(*items)[j]];
7668 if (items_dst != NULL) {
7669 for (j = 0; j < *nitems_dst * nsize; j++) {
7670 (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
7674 IntGroupClear(move_g);
7675 IntGroupRelease(del_g);
7677 IntGroupRelease(move_g);
7679 /* Copy the residues */
7681 /* src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset) */
7682 n1 = src->nresidues - resSeqOffset; /* This will be dst->nresidues (if >0) */
7683 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
7686 memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
7690 /* Copy the parameters to dst */
7691 if (dst != NULL && dst_par_g != NULL && (n2 = IntGroupGetCount(dst_par_g)) > 0) {
7692 IntGroup *dst_new_g = IntGroupNew();
7693 Int dst_par_count[kLastParType - kFirstParType + 1];
7694 if (dst_new_g == NULL)
7696 for (i = 0; i <= kLastParType - kFirstParType; i++)
7697 dst_par_count[i] = 0;
7698 up = (UnionPar *)calloc(sizeof(UnionPar), n2);
7701 if (ParameterCopy(src->par, kFirstParType, up, dst_par_g) < n2)
7703 /* Renumber the explicit atom indices */
7704 for (i = 0; i < nsrc; i++)
7705 old2new[i] -= nsrcnew; /* new indices for atoms in dst; otherwise negative numbers */
7706 for (i = 0; i < n2; i++) {
7707 /* Renumber the indices, and count the number of parameters for each type */
7708 n1 = kFirstParType + IntGroupGetNthPoint(dst_par_g, i) / kParameterIndexOffset;
7709 dst_par_count[n1 - kFirstParType]++;
7710 ParameterRenumberAtoms(n1, up + i, nsrc, old2new);
7712 for (i = 0; i < nsrc; i++)
7713 old2new[i] += nsrcnew;
7714 if (dst->par == NULL)
7715 dst->par = ParameterNew();
7716 for (i = 0; i <= kLastParType - kFirstParType; i++) {
7717 if (dst_par_count[i] > 0)
7718 IntGroupAdd(dst_new_g, i * kParameterIndexOffset, dst_par_count[i]);
7720 if (ParameterInsert(dst->par, kFirstParType, up, dst_new_g) < n2)
7723 IntGroupRelease(dst_new_g);
7725 IntGroupRelease(dst_par_g);
7727 /* Remove the unused parameter. Note: the parameters that are in remove_par_g and not in
7728 dst_par_g will disappear. To support undo, these parameters should be taken care separately. */
7729 if (forUndo == 0 && remove_par_g != NULL && (n2 = IntGroupGetCount(remove_par_g)) > 0) {
7730 UnionPar *up = (UnionPar *)malloc(sizeof(UnionPar) * n2);
7731 ParameterDelete(src->par, kFirstParType, up, remove_par_g);
7732 if (nactions != NULL) {
7733 act = MolActionNew(gMolActionAddParameters, kFirstParType, remove_par_g, n2, up);
7734 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7739 IntGroupRelease(remove_par_g);
7741 /* Renumber the parameter records remaining in the src */
7743 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7744 n2 = ParameterGetCountForType(src->par, n1);
7745 for (i = 0; i < n2; i++) {
7746 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7747 ParameterRenumberAtoms(n1, up, nsrc, old2new);
7753 IntGroupRelease(remain_g);
7754 MoleculeCleanUpResidueTable(src);
7756 MoleculeCleanUpResidueTable(dst);
7759 src->nframes = -1; /* Should be recalculated later */
7761 dst->nframes = -1; /* Should be recalculated later */
7767 MoleculeIncrementModifyCount(src);
7768 src->needsMDRebuild = 1;
7769 __MoleculeUnlock(src);
7774 __MoleculeUnlock(src);
7775 /* Panic("Low memory while removing atoms"); */
7779 /* Separate molecule into two parts. The atoms specified by 'where' are moved
7780 from src to a new molecule, which is returned as *dstp. Dstp can be NULL,
7781 in which case the moved atoms are discarded. */
7783 MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
7785 return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1, nactions, actions, forUndo);
7788 /* Extract atoms from a given molecule into two parts. The atoms specified by
7789 'where' are copied from src to a new molecule, which is returned as *dstp.
7790 If dummyFlag is non-zero, then the atoms that are not included in the group
7791 but are connected to any atoms in the group are converted to "dummy" atoms
7792 (i.e. with element "Du" and names beginning with an underscore) and included
7793 in the new molecule object. */
7795 MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
7799 /* Extract the fragment */
7800 retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0, NULL, NULL, 0);
7806 /* Search bonds crossing the molecule border */
7807 IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
7809 IntGroupIterator iter;
7812 IntGroupIteratorInit(ig, &iter);
7813 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7814 /* The atoms at the border */
7817 n1 = src->bonds[i*2];
7818 n2 = src->bonds[i*2+1];
7819 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
7823 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
7824 continue; /* Actually this is an internal error */
7826 /* n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule */
7827 /* Create a new dummy atom with the same segment/residue info with n1
7828 and the same position as n2 */
7829 ap = ATOM_AT_INDEX(src->atoms, n1);
7830 memset(&a, 0, gSizeOfAtomRecord);
7831 a.segSeq = ap->segSeq;
7832 memmove(a.segName, ap->segName, 4);
7833 a.resSeq = ap->resSeq;
7834 memmove(a.resName, ap->resName, 4);
7835 ElementToString(0, a.element); /* "Du" */
7836 snprintf(a.aname, 4, "_%d", idx++);
7837 a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
7838 /* Add the dummy atom to the new molecule; nn[1] is the index
7839 of the new dummy atom in the new molecule */
7840 nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
7841 /* Connect nn1 and nn2 */
7842 nn[2] = kInvalidIndex;
7843 MoleculeAddBonds(*dstp, 1, nn, NULL, 1);
7845 IntGroupIteratorRelease(&iter);
7846 IntGroupRelease(ig);
7854 MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds, IntGroup *where, Int autoGenerate)
7856 Int nangles, ndihedrals;
7857 Int *angles, *dihedrals;
7858 Int i, j, k, kk, n1, n2, cn1, cn2;
7861 Atom *ap1, *ap2, *ap3;
7863 if (mp == NULL || bonds == NULL || nbonds <= 0)
7865 if (mp->noModifyTopology)
7866 return -4; /* Prohibited operation */
7868 /* Note: Duplicates and validity are not checked (the caller must do that) */
7873 if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, n1 + nbonds - 1, NULL) == NULL
7874 || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nbonds, sizeof(Int) * 2, where) != 0) {
7875 __MoleculeUnlock(mp);
7876 return -4; /* Out of memory */
7878 if (mp->bondOrders != NULL) {
7879 /* Expand the bond order info (all new entries are zero) */
7880 Double *dp = (Double *)calloc(sizeof(Double), nbonds);
7883 if (AssignArray(&(mp->bondOrders), &(mp->nbondOrders), sizeof(Double), n1 + nbonds - 1, NULL) == NULL
7884 || sInsertElementsToArrayAtPositions(mp->bondOrders, n1, dp, nbonds, sizeof(Double), where) != 0) {
7885 __MoleculeUnlock(mp);
7892 angles = dihedrals = NULL;
7893 nangles = ndihedrals = 0;
7895 /* Add connects[], and angles/dihedrals (if autoGenerate is true) */
7896 for (i = 0; i < nbonds; i++) {
7898 /* One entry at time */
7899 /* (Otherwise, duplicate entries of angles and dihedrals result) */
7901 n2 = bonds[i * 2 + 1];
7903 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
7904 AtomConnectInsertEntry(&ap1->connect, -1, n2);
7905 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
7906 AtomConnectInsertEntry(&ap2->connect, -1, n1);
7908 /* Add angles and dihedrals */
7910 AtomConnect *ac1, *ac2;
7911 if (ap1->anchor == NULL || ap2->anchor == NULL) {
7912 /* N1-N2-{XY} or N2-N1-{XY} angles (X: connected atom, Y: constitute atom of pi-anchor) */
7913 for (j = 0; j < 4; j++) {
7915 case 0: temp[0] = n1; temp[1] = n2; ac1 = &ap2->connect; break; /* N1-N2-X */
7916 case 1: if (ap2->anchor == NULL) continue; else ac1 = &ap2->anchor->connect; break; /* N1-N2-Y */
7917 case 2: temp[0] = n2; temp[1] = n1; ac1 = &ap1->connect; break; /* N2-N1-X */
7918 case 3: if (ap1->anchor == NULL) continue; else ac1 = &ap1->anchor->connect; break; /* N2-N1-Y */
7920 cp1 = AtomConnectData(ac1);
7922 for (k = 0; k < cn1; k++) {
7924 if (temp[2] == temp[0])
7926 ap3 = ATOM_AT_INDEX(mp->atoms, temp[2]);
7927 if (ap3->anchor != NULL) {
7928 /* Avoid X-anchor-anchor angle (anchor-X-anchor is allowed) */
7929 if ((j < 2 && ap2->anchor != NULL) || (j >= 2 && ap1->anchor != NULL))
7932 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7934 /* Dihedrals N1-N2-X-{XY} or N2-N1-X-{XY} */
7935 if (j == 1 || j == 3)
7937 cp2 = AtomConnectData(&ap3->connect);
7938 for (kk = 0; kk < ap3->connect.count; kk++) {
7940 if (temp[3] == temp[0] || temp[3] == temp[1])
7942 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7945 if (ap3->anchor != NULL) {
7946 /* N1-N2-X-Y or N2-N1-X-Y */
7947 /* for Y, only the first constitute atom is considered */
7948 cp2 = AtomConnectData(&ap3->anchor->connect);
7950 if (temp[3] == temp[0] || temp[3] == temp[1])
7952 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7958 /* X-N1-N2-X dihedrals */
7959 /* Y-N1-N2-anchor is allowed, but the force may be zero if the angle N1-N2-anchor is */
7960 /* close to 180 deg (e.g. in ferrocene, C-anchor-Fe-anchor dihedral should be k=0) */
7961 if (ap1->anchor == NULL) {
7962 ac1 = &ap1->connect;
7965 ac1 = &ap1->anchor->connect;
7966 cn1 = 1; /* Only the first constitute atom of pi-anchor is considered */
7968 if (ap2->anchor == NULL) {
7969 ac2 = &ap2->connect;
7972 ac2 = &ap2->anchor->connect;
7973 cn2 = 1; /* Only the first constitute atom of pi-anchor is considered */
7977 cp1 = AtomConnectData(ac1);
7978 cp2 = AtomConnectData(ac2);
7979 for (j = 0; j < cn1; j++) {
7981 if (temp[0] == temp[2])
7983 for (k = 0; k < cn2; k++) {
7985 if (temp[3] == temp[0] || temp[3] == temp[1])
7987 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7994 if (angles != NULL) {
7995 temp[0] = kInvalidIndex;
7996 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7998 MoleculeAddAngles(mp, angles, NULL);
8001 if (dihedrals != NULL) {
8002 temp[0] = kInvalidIndex;
8003 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8005 MoleculeAddDihedrals(mp, dihedrals, NULL);
8009 MoleculeIncrementModifyCount(mp);
8010 mp->needsMDRebuild = 1;
8011 __MoleculeUnlock(mp);
8016 __MoleculeUnlock(mp);
8017 Panic("Low memory while adding bonds");
8018 return -1; /* Not reached */
8022 /* The deleted angles and dihedrals are stored in outRemoval. */
8023 /* (*outRemoval) is an array of integers, containing:
8024 [0..na*3-1]: the angle indices
8025 [na*3..na*3+nd*4-1]: the dihedral indices
8026 [na*3+nd*4..na*3+nd*4+ni*4-1]: the improper indices
8027 *outRemovedPos is an intgroup denoting the positions of the removed angles/dihedrals/impropers.
8028 the angle indices are included as they are,
8029 the dihedral indices are offset by ATOMS_MAX_NUMBER,
8030 the improper indices are offset by ATOMS_MAX_NUMBER*2.
8031 Note: the removed bond indices are not returned, because the caller should already know them. */
8033 MoleculeDeleteBonds(Molecule *mp, Int *bonds, IntGroup *where, Int **outRemoved, IntGroup **outRemovedPos)
8035 Int i, j, n1, n2, nw;
8036 Int *ip, *jp, na, nd, ni;
8037 IntGroup *ag, *dg, *ig;
8039 IntGroupIterator iter;
8043 if (mp->noModifyTopology)
8044 return -4; /* Prohibited operation */
8048 /* Update connects[] */
8049 IntGroupIteratorInit(where, &iter);
8050 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8051 n1 = mp->bonds[i * 2];
8052 n2 = mp->bonds[i * 2 + 1];
8053 ap = ATOM_AT_INDEX(mp->atoms, n1);
8054 ip = AtomConnectData(&ap->connect);
8055 for (j = 0; j < ap->connect.count; j++) {
8057 AtomConnectDeleteEntry(&ap->connect, j);
8061 ap = ATOM_AT_INDEX(mp->atoms, n2);
8062 ip = AtomConnectData(&ap->connect);
8063 for (j = 0; j < ap->connect.count; j++) {
8065 AtomConnectDeleteEntry(&ap->connect, j);
8071 /* Remove bonds, angles, dihedrals, impropers */
8076 nw = IntGroupGetCount(where);
8077 jp = (Int *)malloc(sizeof(Int) * nw * 2);
8079 IntGroupIteratorReset(&iter);
8080 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8081 jp[j++] = mp->bonds[i * 2];
8082 jp[j++] = mp->bonds[i * 2 + 1];
8084 IntGroupIteratorRelease(&iter);
8086 for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8087 for (j = 0; j < nw; j++) {
8090 if ((ip[0] == n1 && ip[1] == n2)
8091 || (ip[1] == n1 && ip[0] == n2)
8092 || (ip[1] == n1 && ip[2] == n2)
8093 || (ip[2] == n1 && ip[1] == n2)) {
8094 if (IntGroupAdd(ag, i, 1) != 0)
8101 for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8102 for (j = 0; j < nw; j++) {
8105 if ((ip[0] == n1 && ip[1] == n2)
8106 || (ip[1] == n1 && ip[0] == n2)
8107 || (ip[1] == n1 && ip[2] == n2)
8108 || (ip[2] == n1 && ip[1] == n2)
8109 || (ip[2] == n1 && ip[3] == n2)
8110 || (ip[3] == n1 && ip[2] == n2)) {
8113 if (IntGroupAdd(dg, i, 1) != 0)
8120 for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8121 for (j = 0; j < nw; j++) {
8124 if ((ip[0] == n1 && ip[2] == n2)
8125 || (ip[1] == n1 && ip[2] == n2)
8126 || (ip[3] == n1 && ip[2] == n2)
8127 || (ip[0] == n2 && ip[2] == n1)
8128 || (ip[1] == n2 && ip[2] == n1)
8129 || (ip[3] == n2 && ip[2] == n1)) {
8132 if (IntGroupAdd(ig, i, 1) != 0)
8141 if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, bonds, sizeof(Int) * 2, where) != 0)
8143 mp->nbonds -= IntGroupGetCount(where);
8144 if (mp->nbonds == 0) {
8148 if (mp->bondOrders != NULL) {
8149 if (sRemoveElementsFromArrayAtPositions(mp->bondOrders, mp->nbondOrders, NULL, sizeof(Double), where) != 0)
8151 mp->nbondOrders -= IntGroupGetCount(where);
8152 if (mp->nbondOrders == 0) {
8153 free(mp->bondOrders);
8154 mp->bondOrders = NULL;
8157 if (na == 0 && nd == 0 && ni == 0)
8160 ip = (Int *)malloc(sizeof(Int) * (na * 3 + nd * 4 + ni * 4));
8162 MoleculeDeleteAngles(mp, ip, ag);
8164 MoleculeDeleteDihedrals(mp, ip + na * 3, dg);
8166 MoleculeDeleteImpropers(mp, ip + na * 3 + nd * 4, ig);
8168 IntGroupOffset(dg, ATOMS_MAX_NUMBER);
8169 IntGroupOffset(ig, ATOMS_MAX_NUMBER * 2);
8170 IntGroupAddIntGroup(ag, dg);
8171 IntGroupAddIntGroup(ag, ig);
8172 IntGroupRelease(dg);
8173 IntGroupRelease(ig);
8176 if (IntGroupGetCount(ag) == 0) {
8177 IntGroupRelease(ag);
8182 *outRemovedPos = ag;
8184 MoleculeIncrementModifyCount(mp);
8185 mp->needsMDRebuild = 1;
8186 __MoleculeUnlock(mp);
8188 return na * 3 + nd * 4 + ni * 4;
8191 __MoleculeUnlock(mp);
8192 Panic("Low memory while removing bonds");
8193 return -1; /* Not reached */
8197 MoleculeAssignBondOrders(Molecule *mp, const Double *orders, IntGroup *where)
8200 IntGroupIterator iter;
8201 if (mp == NULL || orders == NULL || mp->nbonds == 0)
8203 if (mp->noModifyTopology)
8204 return -4; /* Prohibited operation */
8205 if (mp->bondOrders == NULL) {
8206 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbonds - 1, NULL);
8207 memset(mp->bondOrders, 0, sizeof(Double) * mp->nbondOrders);
8209 IntGroupIteratorInit(where, &iter);
8211 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8212 if (i >= mp->nbondOrders)
8214 mp->bondOrders[i] = orders[j++];
8216 IntGroupIteratorRelease(&iter);
8221 MoleculeGetBondOrders(Molecule *mp, Double *outOrders, IntGroup *where)
8224 IntGroupIterator iter;
8225 if (mp == NULL || mp->nbonds == 0)
8227 if (mp->bondOrders == NULL) {
8228 /* Returns all zero */
8229 i = IntGroupGetCount(where);
8230 for (j = 0; j < i; j++)
8233 IntGroupIteratorInit(where, &iter);
8235 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8236 if (i < mp->nbondOrders)
8237 outOrders[j] = mp->bondOrders[i];
8238 else outOrders[j] = 0.0;
8246 MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
8249 if (mp == NULL || angles == NULL)
8251 if (mp->noModifyTopology)
8252 return -4; /* Prohibited operation */
8256 nc = IntGroupGetCount(where);
8258 for (n1 = 0; angles[n1 * 3] >= 0; n1++)
8264 if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
8265 || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
8266 __MoleculeUnlock(mp);
8267 Panic("Low memory while adding angles");
8270 mp->needsMDRebuild = 1;
8271 __MoleculeUnlock(mp);
8276 MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
8279 if (mp == NULL || where == NULL)
8281 if (mp->noModifyTopology)
8282 return -4; /* Prohibited operation */
8284 if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
8285 __MoleculeUnlock(mp);
8286 Panic("Bad argument while deleting angles");
8288 mp->nangles -= (nc = IntGroupGetCount(where));
8289 if (mp->nangles == 0) {
8293 mp->needsMDRebuild = 1;
8294 __MoleculeUnlock(mp);
8299 MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
8302 if (mp == NULL || dihedrals == NULL)
8304 if (mp->noModifyTopology)
8305 return -4; /* Prohibited operation */
8307 nc = IntGroupGetCount(where);
8309 for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
8315 n1 = mp->ndihedrals;
8317 if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8318 || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
8319 __MoleculeUnlock(mp);
8320 Panic("Low memory while adding dihedrals");
8322 mp->needsMDRebuild = 1;
8323 __MoleculeUnlock(mp);
8328 MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
8331 if (mp == NULL || where == NULL)
8333 if (mp->noModifyTopology)
8334 return -4; /* Prohibited operation */
8336 if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
8337 __MoleculeUnlock(mp);
8338 Panic("Internal error: bad argument while deleting dihedrals");
8340 mp->ndihedrals -= (nc = IntGroupGetCount(where));
8341 if (mp->ndihedrals == 0) {
8342 free(mp->dihedrals);
8343 mp->dihedrals = NULL;
8345 mp->needsMDRebuild = 1;
8346 __MoleculeUnlock(mp);
8351 MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
8354 if (mp == NULL || impropers == NULL)
8356 if (mp->noModifyTopology)
8357 return -4; /* Prohibited operation */
8359 nc = IntGroupGetCount(where);
8361 for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
8367 n1 = mp->nimpropers;
8369 if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8370 || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
8371 __MoleculeUnlock(mp);
8372 Panic("Low memory while adding impropers");
8374 mp->needsMDRebuild = 1;
8375 __MoleculeUnlock(mp);
8380 MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
8383 if (mp == NULL || where == NULL)
8385 if (mp->noModifyTopology)
8386 return -4; /* Prohibited operation */
8388 if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
8389 __MoleculeUnlock(mp);
8390 Panic("Internal error: bad argument while deleting impropers");
8392 mp->nimpropers -= (nc = IntGroupGetCount(where));
8393 if (mp->impropers == NULL) {
8394 free(mp->impropers);
8395 mp->impropers = NULL;
8397 __MoleculeUnlock(mp);
8402 MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
8405 if (mp == NULL || mp->bonds == NULL)
8407 for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
8408 if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
8415 MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
8418 if (mp == NULL || mp->angles == NULL)
8420 for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8421 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
8422 (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
8429 MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8432 if (mp == NULL || mp->dihedrals == NULL)
8434 for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8435 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
8436 (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
8443 MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8446 if (mp == NULL || mp->impropers == NULL)
8448 for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8451 if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
8452 (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
8453 (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
8459 /* Remove the bond at bondIndex and create two dummy atoms instead.
8460 The dummy atoms are placed at the end of atoms[], and the residue
8461 numbers are the same as the root atoms (i.e. the atoms to which
8462 the dummy atoms are connected). The indices are returned in
8463 dummyIndices[0,1]. */
8465 MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
8467 Int roots[3], newBonds[5];
8473 if (mp == NULL || mp->noModifyTopology)
8475 if (bondIndex < 0 || bondIndex >= mp->nbonds)
8477 roots[0] = mp->bonds[bondIndex * 2];
8478 roots[1] = mp->bonds[bondIndex * 2 + 1];
8479 roots[2] = kInvalidIndex;
8480 rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
8481 rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
8482 VecSub(dr, rootp[0]->r, rootp[1]->r);
8483 for (i = 0; i < 2; i++) {
8486 memmove(nap, rootp[i], sizeof(na));
8487 nap->aname[0] = '*';
8488 strcpy(nap->element, "Du");
8490 nap->charge = nap->weight = 0.0;
8491 nap->atomicNumber = 0;
8492 nap->connect.count = 0;
8493 w = (i == 0 ? 0.4 : -0.4);
8494 VecScaleInc(nap->r, dr, w);
8501 /* Expand atoms array and append the dummy atoms at the end */
8503 natoms = mp->natoms;
8504 if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
8506 memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
8507 dummyIndices[0] = natoms;
8508 dummyIndices[1] = natoms + 1;
8510 /* Remove the old bond and create new bonds */
8511 ig = IntGroupNewWithPoints(bondIndex, 1, -1);
8514 MoleculeDeleteBonds(mp, NULL, ig, NULL, NULL);
8515 IntGroupRelease(ig);
8516 newBonds[0] = roots[0];
8517 newBonds[1] = dummyIndices[0];
8518 newBonds[2] = roots[1];
8519 newBonds[3] = dummyIndices[1];
8520 newBonds[4] = kInvalidIndex;
8522 i = (MoleculeAddBonds(mp, 2, newBonds, NULL, 1) < 0 ? -1 : 0);
8523 mp->needsMDRebuild = 1;
8524 __MoleculeUnlock(mp);
8528 __MoleculeUnlock(mp);
8529 Panic("Low memory during creating dummy atoms");
8533 /* Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
8534 a bond between the two root atoms. The value bondIndex is used as a
8535 hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
8536 the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
8537 is ignored and the new bond is stored at the end of bonds[]. */
8539 MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
8546 MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
8549 if (mol == NULL || mol->noModifyTopology)
8556 __MoleculeLock(mol);
8557 NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
8558 memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
8559 mol->needsMDRebuild = 1;
8560 __MoleculeUnlock(mol);
8567 MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
8570 if (mol == NULL || mol->noModifyTopology)
8572 n1 = mol->ndihedrals;
8573 np1 = mol->dihedrals;
8574 mol->ndihedrals = 0;
8575 mol->dihedrals = NULL;
8576 if (ndihedrals > 0) {
8577 __MoleculeLock(mol);
8578 NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
8579 memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
8580 mol->needsMDRebuild = 1;
8581 __MoleculeUnlock(mol);
8583 *outDihedrals = np1;
8588 MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
8591 if (mol == NULL || mol->noModifyTopology)
8593 n1 = mol->nimpropers;
8594 np1 = mol->impropers;
8595 mol->nimpropers = 0;
8596 mol->impropers = NULL;
8597 if (nimpropers > 0) {
8598 __MoleculeLock(mol);
8599 NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
8600 memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
8601 mol->needsMDRebuild = 1;
8602 __MoleculeUnlock(mol);
8604 *outImpropers = np1;
8610 MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
8617 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8618 return 0; /* molecule is empty */
8619 if (mol->noModifyTopology)
8623 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
8624 Int *cp = AtomConnectData(&ap->connect);
8625 if (ap->anchor != NULL)
8627 for (j = 0; j < ap->connect.count; j++) {
8629 if (ATOM_AT_INDEX(mol->atoms, j0)->anchor != NULL)
8631 for (k = j + 1; k < ap->connect.count; k++) {
8633 if (ATOM_AT_INDEX(mol->atoms, k0)->anchor != NULL)
8635 if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
8636 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
8645 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
8649 if (outAngles != NULL)
8650 *outAngles = angles;
8655 MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
8657 Int n1, n2, n3, n4, *ip, *cp2, *cp3;
8662 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8663 return 0; /* molecule is empty */
8666 for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
8667 Int i1, i3, i4, *ip;
8668 if (ap2->anchor != NULL)
8670 cp2 = AtomConnectData(&ap2->connect);
8671 for (i3 = 0; i3 < ap2->connect.count; i3++) {
8675 ap3 = ATOM_AT_INDEX(mol->atoms, n3);
8676 if (ap3->anchor != NULL)
8678 cp3 = AtomConnectData(&ap3->connect);
8679 for (i1 = 0; i1 < ap2->connect.count; i1++) {
8683 if (ATOM_AT_INDEX(mol->atoms, n1)->anchor != NULL)
8685 for (i4 = 0; i4 < ap3->connect.count; i4++) {
8687 if (n2 == n4 || n1 == n4)
8689 if (ATOM_AT_INDEX(mol->atoms, n4)->anchor != NULL)
8691 if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
8692 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
8702 if (ndihedrals > 0) {
8703 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
8707 if (outDihedrals != NULL)
8708 *outDihedrals = dihedrals;
8713 MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
8715 Int n1, n2, n3, n4, t1, t2, t3, t4, *ip, *cp;
8716 Parameter *par = mol->par;
8721 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8722 return 0; /* molecule is empty */
8723 if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
8724 return 0; /* No improper parameters are defined */
8728 for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
8729 Int i1, i2, i4, found, *ip;
8731 cp = AtomConnectData(&ap3->connect);
8732 for (i1 = 0; i1 < ap3->connect.count; i1++) {
8734 t1 = ATOM_AT_INDEX(ap, n1)->type;
8735 for (i2 = i1 + 1; i2 < ap3->connect.count; i2++) {
8737 t2 = ATOM_AT_INDEX(ap, n2)->type;
8738 for (i4 = i2 + 1; i4 < ap3->connect.count; i4++) {
8740 t4 = ATOM_AT_INDEX(ap, n4)->type;
8742 if (ParameterLookupImproperPar(par, t1, t2, t3, t4, n1, n2, n3, n4, 0) != NULL)
8744 else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, -1, -1, -1, -1, 0) != NULL)
8746 if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
8747 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8757 if (nimpropers > 0) {
8758 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8762 if (outImpropers != NULL)
8763 *outImpropers = impropers;
8767 #pragma mark ====== Residues ======
8770 MoleculeCleanUpResidueTable(Molecule *mp)
8774 if (mp == NULL || mp->natoms == 0)
8778 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8779 if (ap->resSeq >= maxres)
8780 maxres = ap->resSeq + 1;
8781 if (ap->resSeq < mp->nresidues) {
8782 if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
8783 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8785 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
8788 if (maxres < mp->nresidues)
8789 mp->nresidues = maxres;
8790 __MoleculeUnlock(mp);
8793 /* Change the number of residues. If nresidues is greater than the current value,
8794 then the array mp->residues is expanded with null names. If nresidues is smaller
8795 than the current value, mp->nresidues is set to the smallest possible value
8796 that is no smaller than nresidues and larger than any of the resSeq values. */
8798 MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
8803 if (mp->nresidues == nresidues)
8805 else if (mp->nresidues < nresidues) {
8808 AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
8809 while (n < nresidues)
8810 mp->residues[n++][0] = 0;
8811 __MoleculeUnlock(mp);
8817 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8818 if (ap->resSeq >= n)
8827 MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
8829 IntGroupIterator iter;
8830 int withArray, resSeq, maxSeq;
8834 /* If LSB of resSeqs is 1, then a constant value is used for all specified atoms */
8835 if (((int)resSeqs & 1) == 0) {
8840 resSeq = ((int)resSeqs - 1) / 2;
8843 IntGroupIteratorInit(group, &iter);
8845 /* Change resSeqs */
8849 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8850 ap = ATOM_AT_INDEX(mp->atoms, i);
8852 resSeq = resSeqs[j++];
8853 if (resSeq > maxSeq)
8855 ap->resSeq = resSeq;
8857 __MoleculeUnlock(mp);
8859 /* Expand array if necessary */
8860 if (maxSeq >= mp->nresidues)
8861 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8863 /* Synchronize resName and residues[] */
8865 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8866 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8868 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8870 IntGroupIteratorRelease(&iter);
8871 __MoleculeUnlock(mp);
8873 MoleculeIncrementModifyCount(mp);
8879 MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
8881 return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(resSeq * 2 + 1));
8884 /* Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
8885 specifies the mp->nresidues after modifying the residue numbers.
8886 If all atoms are modified, then the table of residue names is also shifted. Otherwise,
8887 the table of residue names is not touched. */
8889 MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
8891 int i, maxSeq, nmodatoms;
8893 IntGroupIterator iter;
8894 IntGroupIteratorInit(group, &iter);
8897 nresidues = mp->nresidues;
8900 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8901 ap = ATOM_AT_INDEX(mp->atoms, i);
8902 ap->resSeq += offset;
8903 if (ap->resSeq < 0) {
8904 /* Bad argument; undo change and returns this index + 1 */
8906 ap->resSeq -= offset;
8907 while ((i = IntGroupIteratorLast(&iter)) >= 0) {
8908 ap = ATOM_AT_INDEX(mp->atoms, i);
8909 ap->resSeq -= offset;
8911 IntGroupIteratorRelease(&iter);
8912 return bad_index + 1;
8914 if (ap->resSeq > maxSeq)
8915 maxSeq = ap->resSeq;
8918 if (maxSeq >= nresidues)
8919 nresidues = maxSeq + 1;
8920 if (offset < 0 && nmodatoms == mp->natoms) {
8921 /* Shift the residue names downward */
8922 memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
8924 __MoleculeUnlock(mp);
8925 MoleculeChangeNumberOfResidues(mp, nresidues);
8926 if (offset > 0 && nmodatoms == mp->natoms) {
8927 /* Shift the residue names upward */
8929 memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
8930 __MoleculeUnlock(mp);
8932 IntGroupIteratorRelease(&iter);
8934 MoleculeIncrementModifyCount(mp);
8939 /* Change residue names for the specified residue numbers. Names is an array of
8940 chars containing argc*4 characters, and every 4 characters represent a
8941 residue name; characters '\x01'-'\x1f' are converted to '\0', which allow
8942 names to be handled as a C string. */
8944 MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
8949 for (i = 0; i < argc; i++) {
8950 if (maxSeq < resSeqs[i])
8951 maxSeq = resSeqs[i];
8953 if (maxSeq >= mp->nresidues)
8954 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8956 for (i = 0; i < argc; i++) {
8957 char *p = mp->residues[resSeqs[i]];
8959 strncpy(p, names + i * 4, 4);
8960 for (j = 0; j < 4; j++) {
8961 if (p[j] >= 0 && p[j] < 0x20)
8965 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8966 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8968 __MoleculeUnlock(mp);
8970 MoleculeIncrementModifyCount(mp);
8975 /* Returns the maximum residue number actually used */
8977 MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
8982 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8983 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8985 if (ap->resSeq > maxSeq)
8986 maxSeq = ap->resSeq;
8991 /* Returns the minimum residue number actually used */
8993 MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
8997 minSeq = ATOMS_MAX_NUMBER;
8998 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8999 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9001 if (ap->resSeq < minSeq)
9002 minSeq = ap->resSeq;
9004 return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
9007 #pragma mark ====== Sort by Residues ======
9010 sAtomSortComparator(const void *a, const void *b)
9012 const Atom *ap, *bp;
9013 ap = *((const Atom **)a);
9014 bp = *((const Atom **)b);
9015 if (ap->resSeq == bp->resSeq) {
9016 /* Retain the original order (i.e. atom with larger pointer address is larger) */
9023 /* Compare the residue sequence. However, residue sequence 0 is always larger. */
9024 if (ap->resSeq == 0)
9026 else if (bp->resSeq == 0)
9028 else if (ap->resSeq < bp->resSeq)
9030 else if (ap->resSeq > bp->resSeq)
9037 sMoleculeReorder(Molecule *mp)
9039 int i, res, prevRes;
9043 if (mp == NULL || mp->natoms <= 1)
9046 /* Sort the atoms, bonds, etc. */
9047 apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
9048 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9049 newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9050 if (apArray == NULL || old2new == NULL || newAtoms == NULL)
9051 Panic("Low memory during reordering atoms");
9052 for (i = 0; i < mp->natoms; i++)
9053 apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
9055 /* Sort the atoms. Note: apArray is an array of "Pointer to Atom" */
9056 qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
9058 /* Make a table of 'which atom becomes which' */
9059 for (i = 0; i < mp->natoms; i++) {
9060 int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
9064 /* Renumber the bonds, etc. */
9065 for (i = 0; i < mp->nbonds * 2; i++) {
9066 mp->bonds[i] = old2new[mp->bonds[i]];
9068 for (i = 0; i < mp->nangles * 3; i++) {
9069 mp->angles[i] = old2new[mp->angles[i]];
9071 for (i = 0; i < mp->ndihedrals * 4; i++) {
9072 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9074 for (i = 0; i < mp->nimpropers * 4; i++) {
9075 mp->impropers[i] = old2new[mp->impropers[i]];
9077 for (i = 0; i < mp->natoms; i++) {
9079 ip = AtomConnectData(&(apArray[i]->connect));
9080 for (j = 0; j < apArray[i]->connect.count; j++, ip++)
9084 /* Renumber the residues so that the residue numbers are contiguous */
9086 for (i = 0; i < mp->natoms; i++) {
9087 if (apArray[i]->resSeq == 0)
9089 if (apArray[i]->resSeq != prevRes) {
9091 prevRes = apArray[i]->resSeq;
9092 if (prevRes != res) {
9093 strncpy(mp->residues[res], mp->residues[prevRes], 4);
9096 apArray[i]->resSeq = res;
9098 mp->nresidues = res + 1;
9100 /* Sort the atoms and copy back to atoms[] */
9101 for (i = 0; i < mp->natoms; i++) {
9102 memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
9104 memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
9106 /* Free the locally allocated storage */
9112 /* Renumber atoms */
9114 MoleculeRenumberAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
9116 Int *old2new, i, j, retval;
9120 if (mp->noModifyTopology)
9122 if (old2new_out != NULL)
9123 old2new = old2new_out;
9125 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9126 saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9127 if (old2new == NULL || saveAtoms == NULL)
9128 Panic("Low memory during reordering atoms");
9129 memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
9131 for (i = 0; i < mp->natoms; i++)
9133 for (i = 0; i < isize && i < mp->natoms; i++) {
9135 if (j < 0 || j >= mp->natoms) {
9136 retval = 1; /* Out of range */
9139 if (old2new[j] != -1) {
9140 retval = 2; /* Duplicate entry */
9145 if (i < mp->natoms) {
9146 for (j = 0; j < mp->natoms; j++) {
9147 if (old2new[j] != -1)
9152 if (i != mp->natoms) {
9153 retval = 3; /* Internal inconsistency */
9157 /* Renumber the bonds, etc. */
9158 for (i = 0; i < mp->nbonds * 2; i++) {
9159 mp->bonds[i] = old2new[mp->bonds[i]];
9161 for (i = 0; i < mp->nangles * 3; i++) {
9162 mp->angles[i] = old2new[mp->angles[i]];
9164 for (i = 0; i < mp->ndihedrals * 4; i++) {
9165 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9167 for (i = 0; i < mp->nimpropers * 4; i++) {
9168 mp->impropers[i] = old2new[mp->impropers[i]];
9170 /* Renumber the connection table and pi anchor table */
9171 for (i = 0; i < mp->natoms; i++) {
9172 Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
9173 Int *ip = AtomConnectData(&ap->connect);
9174 for (j = 0; j < ap->connect.count; j++, ip++)
9176 if (ap->anchor != NULL) {
9177 ip = AtomConnectData(&ap->anchor->connect);
9178 for (j = 0; j < ap->anchor->connect.count; j++, ip++)
9183 if (mp->par != NULL) {
9184 /* Renumber the parameters */
9186 for (j = kFirstParType; j <= kLastParType; j++) {
9187 n = ParameterGetCountForType(mp->par, j);
9188 for (i = 0; i < n; i++) {
9189 UnionPar *up = ParameterGetUnionParFromTypeAndIndex(mp->par, j, i);
9191 ParameterRenumberAtoms(j, up, mp->natoms, old2new);
9196 /* Renumber the atoms */
9197 for (i = 0; i < mp->natoms; i++)
9198 memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
9201 MoleculeIncrementModifyCount(mp);
9202 mp->needsMDRebuild = 1;
9205 __MoleculeUnlock(mp);
9207 if (old2new_out == NULL)
9212 #pragma mark ====== Coordinate Transform ======
9215 MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
9220 Transform rtr, symtr;
9221 if (mp == NULL || tr == NULL)
9223 TransformInvert(rtr, tr);
9225 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9226 if (group == NULL || IntGroupLookup(group, i, NULL) != 0) {
9227 TransformVec(&ap->r, tr, &ap->r);
9228 if (!SYMOP_ALIVE(ap->symop))
9230 /* Transform symop */
9231 if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9233 TransformMul(symtr, tr, symtr);
9234 if (group == NULL || IntGroupLookup(group, ap->symbase, NULL) != 0)
9235 TransformMul(symtr, symtr, rtr);
9237 if (!SYMOP_ALIVE(ap->symop))
9239 /* Transform symop if the base atom is transformed */
9240 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
9242 if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9244 TransformMul(symtr, symtr, rtr);
9246 if (MoleculeGetSymopForTransform(mp, symtr, &new_symop, 1) != 0)
9248 ap->symop = new_symop;
9250 mp->needsMDCopyCoordinates = 1;
9251 __MoleculeUnlock(mp);
9252 sMoleculeNotifyChangeAppearance(mp);
9257 MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
9261 if (mp == NULL || tr == NULL)
9264 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9265 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9267 TransformVec(&ap->r, tr, &ap->r);
9269 mp->needsMDCopyCoordinates = 1;
9270 __MoleculeUnlock(mp);
9271 sMoleculeNotifyChangeAppearance(mp);
9276 MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
9279 if (mp == NULL || vp == NULL)
9281 memset(tr, 0, sizeof(tr));
9282 tr[0] = tr[4] = tr[8] = 1.0;
9286 MoleculeTransform(mp, tr, group);
9290 MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
9293 TransformForRotation(tr, axis, angle, center);
9294 MoleculeTransform(mp, tr, group);
9298 MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
9303 if (mp == NULL || center == NULL)
9305 if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9306 return 2; /* Empty molecule */
9308 center->x = center->y = center->z = 0.0;
9309 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9310 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9312 VecScaleInc(*center, ap->r, ap->weight);
9316 return 3; /* Atomic weights are not defined? */
9318 VecScaleSelf(*center, w);
9323 MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
9330 if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9331 return 2; /* Empty molecule */
9332 vmin.x = vmin.y = vmin.z = 1e50;
9333 vmax.x = vmax.y = vmax.z = -1e50;
9334 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9335 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9337 if (vmin.x > ap->r.x)
9339 if (vmin.y > ap->r.y)
9341 if (vmin.z > ap->r.z)
9343 if (vmax.x < ap->r.x)
9345 if (vmax.y < ap->r.y)
9347 if (vmax.z < ap->r.z)
9357 #pragma mark ====== Measurements ======
9360 MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
9363 /* if (mp->is_xtal_coord) {
9364 TransformVec(&r1, mp->cell->tr, vp1);
9365 TransformVec(&r2, mp->cell->tr, vp2);
9371 return VecLength(r1);
9375 MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
9379 /* if (mp->is_xtal_coord) {
9380 TransformVec(&r1, mp->cell->tr, vp1);
9381 TransformVec(&r2, mp->cell->tr, vp2);
9382 TransformVec(&r3, mp->cell->tr, vp3);
9390 w = VecLength(r1) * VecLength(r3);
9393 return acos(VecDot(r1, r3) / w) * kRad2Deg;
9397 MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
9399 Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
9401 /* if (mp->is_xtal_coord) {
9402 TransformVec(&r1, mp->cell->tr, vp1);
9403 TransformVec(&r2, mp->cell->tr, vp2);
9404 TransformVec(&r3, mp->cell->tr, vp3);
9405 TransformVec(&r4, mp->cell->tr, vp4);
9412 VecSub(r21, r1, r2);
9413 VecSub(r32, r2, r3);
9414 VecSub(r43, r3, r4);
9415 VecCross(v1, r21, r32);
9416 VecCross(v2, r32, r43);
9417 VecCross(v3, r32, v1);
9421 if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
9427 VecScaleSelf(v1, w1);
9428 VecScaleSelf(v2, w2);
9429 VecScaleSelf(v3, w3);
9430 return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * kRad2Deg;
9434 #pragma mark ====== XtalCell Parameters ======
9437 MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
9439 if (mp->cell != NULL) {
9440 TransformVec(dst, mp->cell->tr, src);
9445 MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
9447 if (mp->cell != NULL) {
9448 TransformVec(dst, mp->cell->rtr, src);
9453 MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
9455 static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
9457 Vector *vp1, *vp2, *vp3;
9462 for (n1 = 0; n1 < 3; n1++) {
9463 if (cp->flags[n1] != 0)
9467 /* All directions are non-periodic */
9468 memmove(&(cp->tr), &identityTransform, sizeof(Transform));
9469 memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
9473 vp1 = &(cp->axes[n1]);
9474 vp2 = &(cp->axes[n2]);
9475 vp3 = &(cp->axes[n3]);
9476 cp->tr[n1*3] = vp1->x;
9477 cp->tr[n1*3+1] = vp1->y;
9478 cp->tr[n1*3+2] = vp1->z;
9479 cp->tr[9] = cp->origin.x;
9480 cp->tr[10] = cp->origin.y;
9481 cp->tr[11] = cp->origin.z;
9482 if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
9483 /* 1-dimensional or 2-dimensional system */
9484 /* Create "dummy" axes, so that transforms between internal and cartesian coordinates are
9485 possible with a single matrix */
9486 if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
9488 static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
9489 VecCross(v1, *vp1, xvec);
9490 VecCross(v2, *vp1, yvec);
9491 if (VecLength2(v1) < VecLength2(v2))
9493 VecCross(v2, *vp1, v1);
9494 if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
9495 return -1; /* Non-regular transform */
9496 } else if (cp->flags[n2] == 0) {
9498 VecCross(v1, v2, *vp1);
9499 if (NormalizeVec(&v1, &v1))
9500 return -1; /* Non-regular transform */
9503 VecCross(v2, *vp1, v1);
9504 if (NormalizeVec(&v2, &v2))
9505 return -1; /* Non-regular transform */
9507 cp->tr[n2*3] = v1.x;
9508 cp->tr[n2*3+1] = v1.y;
9509 cp->tr[n2*3+2] = v1.z;
9510 cp->tr[n3*3] = v2.x;
9511 cp->tr[n3*3+1] = v2.y;
9512 cp->tr[n3*3+2] = v2.z;
9514 VecCross(v1, *vp1, *vp2);
9515 if (fabs(VecDot(v1, *vp3)) < 1e-7)
9516 return -1; /* Non-regular transform */
9517 cp->tr[n2*3] = vp2->x;
9518 cp->tr[n2*3+1] = vp2->y;
9519 cp->tr[n2*3+2] = vp2->z;
9520 cp->tr[n3*3] = vp3->x;
9521 cp->tr[n3*3+1] = vp3->y;
9522 cp->tr[n3*3+2] = vp3->z;
9525 if (TransformInvert(cp->rtr, cp->tr))
9526 return -1; /* Non-regular transform */
9528 /* Calculate the reciprocal cell parameters */
9529 cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[3] * cp->rtr[3] + cp->rtr[6] * cp->rtr[6]);
9530 cp->rcell[1] = sqrt(cp->rtr[1] * cp->rtr[1] + cp->rtr[4] * cp->rtr[4] + cp->rtr[7] * cp->rtr[7]);
9531 cp->rcell[2] = sqrt(cp->rtr[2] * cp->rtr[2] + cp->rtr[5] * cp->rtr[5] + cp->rtr[8] * cp->rtr[8]);
9532 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;
9533 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;
9534 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;
9537 /* Calculate a, b, c, alpha, beta, gamma */
9538 cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[1] * cp->tr[1] + cp->tr[2] * cp->tr[2]);
9539 cp->cell[1] = sqrt(cp->tr[3] * cp->tr[3] + cp->tr[4] * cp->tr[4] + cp->tr[5] * cp->tr[5]);
9540 cp->cell[2] = sqrt(cp->tr[6] * cp->tr[6] + cp->tr[7] * cp->tr[7] + cp->tr[8] * cp->tr[8]);
9541 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;
9542 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;
9543 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;
9549 MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
9558 memset(&cmat, 0, sizeof(Transform));
9559 if (mp->cell != NULL)
9560 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
9562 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
9564 if (mp->cell != NULL) {
9566 mp->needsMDRebuild = 1;
9572 cp = (XtalCell *)calloc(sizeof(XtalCell), 1);
9574 Panic("Low memory during setting cell parameters");
9576 mp->needsMDRebuild = 1;
9578 /* alpha, beta, gamma are in degree */
9582 cp->cell[3] = alpha;
9584 cp->cell[5] = gamma;
9585 if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
9586 /* c unique (hexagonal etc.) */
9587 Double cosa, cosb, sinb, cosg;
9588 cosa = cos(alpha * kDeg2Rad);
9589 cosb = cos(beta * kDeg2Rad);
9590 sinb = sin(beta * kDeg2Rad);
9591 cosg = cos(gamma * kDeg2Rad);
9592 cp->axes[0].x = a * sinb;
9594 cp->axes[0].z = a * cosb;
9595 cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
9596 cp->axes[1].z = b * cosa;
9597 cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
9603 Double cosg, sing, cosa, cosb;
9604 cosa = cos(alpha * kDeg2Rad);
9605 cosb = cos(beta * kDeg2Rad);
9606 cosg = cos(gamma * kDeg2Rad);
9607 sing = sin(gamma * kDeg2Rad);
9608 cp->axes[0].x = a * sing;
9609 cp->axes[0].y = a * cosg;
9614 cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
9615 cp->axes[2].y = c * cosa;
9616 cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
9618 cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
9619 cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
9620 MoleculeCalculateCellFromAxes(cp, 0);
9621 TransformMul(cmat, cp->tr, cmat);
9624 /* Update the coordinates (if requested) */
9625 if (convertCoordinates) {
9626 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9627 TransformVec(&(ap->r), cmat, &(ap->r));
9631 /* Update the anisotropic parameters */
9632 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9633 Aniso *anp = ap->aniso;
9635 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
9638 __MoleculeUnlock(mp);
9639 sMoleculeNotifyChangeAppearance(mp);
9643 MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x13, Double x23, const Double *sigmaptr)
9647 const Double log2 = 0.693147180559945;
9648 const Double pi22 = 19.7392088021787; /* 2*pi**2 */
9654 if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
9656 anp = mp->atoms[n1].aniso;
9659 anp = (Aniso *)calloc(sizeof(Aniso), 1);
9661 __MoleculeUnlock(mp);
9662 Panic("Low memory during setting anisotropic atom parameters");
9664 mp->atoms[n1].aniso = anp;
9667 case 1: d = 1; dx = 0.5; break;
9668 case 2: d = log2; dx = log2; break;
9669 case 3: d = log2; dx = log2 * 0.5; break;
9670 case 4: u = 1; d = 0.25; dx = 0.25; break;
9671 case 5: u = 1; d = 0.25; dx = 0.125; break;
9672 case 8: u = 1; d = pi22; dx = pi22; break;
9673 case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
9674 case 10: d = pi22; dx = pi22; break;
9675 default: d = dx = 1; break;
9677 anp->bij[0] = x11 * d;
9678 anp->bij[1] = x22 * d;
9679 anp->bij[2] = x33 * d;
9680 anp->bij[3] = x12 * dx;
9681 anp->bij[4] = x13 * dx;
9682 anp->bij[5] = x23 * dx;
9683 if (sigmaptr != NULL) {
9685 anp->bsig[0] = sigmaptr[0] * d;
9686 anp->bsig[1] = sigmaptr[1] * d;
9687 anp->bsig[2] = sigmaptr[2] * d;
9688 anp->bsig[3] = sigmaptr[3] * dx;
9689 anp->bsig[4] = sigmaptr[4] * dx;
9690 anp->bsig[5] = sigmaptr[5] * dx;
9693 anp->bsig[0] = anp->bsig[1] = anp->bsig[2] = anp->bsig[3] = anp->bsig[4] = anp->bsig[5] = 0.0;
9696 if (cp != NULL && u == 1) {
9697 anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
9698 anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
9699 anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
9700 anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
9701 anp->bij[4] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[3] * kDeg2Rad); */
9702 anp->bij[5] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[4] * kDeg2Rad); */
9703 if (sigmaptr != NULL) {
9704 anp->bsig[0] *= cp->rcell[0] * cp->rcell[0];
9705 anp->bsig[1] *= cp->rcell[1] * cp->rcell[1];
9706 anp->bsig[2] *= cp->rcell[2] * cp->rcell[2];
9707 anp->bsig[3] *= cp->rcell[0] * cp->rcell[1];
9708 anp->bsig[4] *= cp->rcell[2] * cp->rcell[0];
9709 anp->bsig[5] *= cp->rcell[1] * cp->rcell[2];
9713 /* Calculate the principal axes (in Cartesian coordinates) */
9714 /* The principal axes are the eigenvectors of matrix At(B^-1)A, where
9715 B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
9716 in which x and z are the crystal-space and cartesian coordinates. */
9717 m1[0] = anp->bij[0] / pi22;
9718 m1[4] = anp->bij[1] / pi22;
9719 m1[8] = anp->bij[2] / pi22;
9720 m1[1] = m1[3] = anp->bij[3] / pi22;
9721 m1[2] = m1[6] = anp->bij[4] / pi22;
9722 m1[5] = m1[7] = anp->bij[5] / pi22;
9723 MatrixInvert(m1, m1);
9725 memmove(m2, cp->rtr, sizeof(Mat33));
9726 MatrixMul(m1, m1, m2);
9727 MatrixTranspose(m2, m2);
9728 MatrixMul(m1, m2, m1);
9730 MatrixSymDiagonalize(m1, val, axis);
9731 for (u = 0; u < 3; u++) {
9733 fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
9736 val[u] = 1 / sqrt(val[u]);
9738 anp->pmat[u*3] = axis[u].x * val[u];
9739 anp->pmat[u*3+1] = axis[u].y * val[u];
9740 anp->pmat[u*3+2] = axis[u].z * val[u];
9742 __MoleculeUnlock(mp);
9745 /* Set the anisotropic parameter for atom idx according to the symop. If symop is not alive, nothing is done. */
9747 MoleculeSetAnisoBySymop(Molecule *mp, int idx)
9751 if (mp == NULL || idx < 0 || idx >= mp->natoms)
9753 ap = ATOM_AT_INDEX(mp->atoms, idx);
9754 if (!SYMOP_ALIVE(ap->symop))
9756 ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
9757 if (ap2->aniso == NULL) {
9758 if (ap->aniso != NULL) {
9764 if (ap->aniso == NULL)
9765 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
9766 if (ap->symop.sym == 0 || ap->symop.sym >= mp->nsyms) {
9767 /* Just copy the aniso parameters */
9768 memmove(ap->aniso, ap2->aniso, sizeof(Aniso));
9771 memmove(t1, SYMMETRY_AT_INDEX(mp->syms, ap->symop.sym), sizeof(Transform));
9772 t1[9] = t1[10] = t1[11] = 0.0;
9773 memset(t2, 0, sizeof(Transform));
9774 t2[0] = ap2->aniso->bij[0];
9775 t2[4] = ap2->aniso->bij[1];
9776 t2[8] = ap2->aniso->bij[2];
9777 t2[1] = t2[3] = ap2->aniso->bij[3];
9778 t2[2] = t2[6] = ap2->aniso->bij[4];
9779 t2[5] = t2[7] = ap2->aniso->bij[5];
9780 TransformMul(t2, t1, t2);
9781 TransformInvert(t1, t1);
9782 TransformMul(t2, t2, t1);
9783 MoleculeSetAniso(mp, idx, 0, t2[0], t2[4], t2[8], t2[1], t2[2], t2[5], (ap2->aniso->has_bsig ? ap2->aniso->bsig : NULL));
9787 MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic, int convertCoordinates)
9789 static Vector zeroVec = {0, 0, 0};
9796 if (mp->cell != NULL)
9797 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
9799 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
9801 if (mp->cell != NULL) {
9803 mp->needsMDRebuild = 1;
9808 memset(&b, 0, sizeof(b));
9809 b.axes[0] = (ax != NULL ? *ax : zeroVec);
9810 b.axes[1] = (ay != NULL ? *ay : zeroVec);
9811 b.axes[2] = (az != NULL ? *az : zeroVec);
9813 memmove(b.flags, periodic, 3);
9814 if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
9817 if (mp->cell == NULL) {
9818 mp->needsMDRebuild = 1;
9820 if (mp->cell->has_sigma) {
9821 /* Keep the sigma */
9823 memmove(b.cellsigma, mp->cell->cellsigma, sizeof(mp->cell->cellsigma));
9825 if ((b.flags[0] != mp->cell->flags[0]) || (b.flags[1] != mp->cell->flags[1]) || (b.flags[2] != mp->cell->flags[2])) {
9826 mp->needsMDRebuild = 1;
9830 mp->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
9831 if (mp->cell != NULL) {
9832 memmove(mp->cell, &b, sizeof(XtalCell));
9833 TransformMul(cmat, b.tr, cmat);
9834 /* Update the coordinates (if requested) */
9835 if (convertCoordinates) {
9836 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9837 TransformVec(&(ap->r), cmat, &(ap->r));
9841 /* Update the anisotropic parameters */
9842 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9843 Aniso *anp = ap->aniso;
9845 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
9849 } else n = -2; /* Out of memory */
9850 __MoleculeUnlock(mp);
9851 sMoleculeNotifyChangeAppearance(mp);
9855 #pragma mark ====== Fragment manipulation ======
9858 sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
9862 if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
9864 IntGroupAdd(result, idx, 1);
9865 ap = ATOM_AT_INDEX(mp->atoms, idx);
9866 cp = AtomConnectData(&ap->connect);
9867 for (i = 0; i < ap->connect.count; i++) {
9869 if (IntGroupLookup(result, idx2, NULL))
9871 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, idx2)->anchor != NULL)
9872 continue; /* bond between two pi_anchors is ignored */
9873 sMoleculeFragmentSub(mp, idx2, result, exatoms);
9875 if (ap->anchor != NULL) {
9876 cp = AtomConnectData(&ap->anchor->connect);
9877 for (i = 0; i < ap->anchor->connect.count; i++) {
9879 if (IntGroupLookup(result, idx2, NULL))
9881 sMoleculeFragmentSub(mp, idx2, result, exatoms);
9886 /* The molecular fragment (= interconnected atoms) containing the atom n1 and
9887 not containing the atoms in exatoms */
9889 MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
9892 if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9894 result = IntGroupNew();
9895 sMoleculeFragmentSub(mp, n1, result, exatoms);
9899 /* The molecular fragment (= interconnected atoms) containing the atom n1 and
9900 not containing the atoms n2, n3, ... (terminated by -1) */
9902 MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
9905 IntGroup *exatoms, *result;
9906 if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9908 exatoms = IntGroupNew();
9909 for (i = 0; i < argc; i++)
9910 IntGroupAdd(exatoms, argv[i], 1);
9911 result = IntGroupNew();
9912 sMoleculeFragmentSub(mp, n1, result, exatoms);
9913 IntGroupRelease(exatoms);
9917 /* The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
9918 not containing the atoms in exatoms */
9920 MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
9922 IntGroupIterator iter;
9925 if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
9927 IntGroupIteratorInit(inatoms, &iter);
9928 result = IntGroupNew();
9929 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9930 sMoleculeFragmentSub(mp, i, result, exatoms);
9932 IntGroupIteratorRelease(&iter);
9936 /* Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
9937 group is bound to the rest of the molecule via only one bond.
9938 If the result is true, then the atoms belonging to the (only) bond are returned
9939 in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
9940 and n2 can be NULL, if those informations are not needed. */
9942 MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
9944 Int i, i1, i2, j, k, bond_count, nval1, nval2, *cp;
9946 if (mp == NULL || mp->natoms == 0 || group == NULL)
9947 return 0; /* Invalid arguments */
9949 for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
9950 i2 = IntGroupGetEndPoint(group, i);
9951 for (j = i1; j < i2; j++) {
9952 if (j < 0 || j >= mp->natoms)
9953 return 0; /* Invalid atom group */
9954 ap = ATOM_AT_INDEX(mp->atoms, j);
9955 cp = AtomConnectData(&ap->connect);
9956 for (k = 0; k < ap->connect.count; k++) {
9957 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, cp[k])->anchor != NULL)
9958 continue; /* Ignore bond between two pi_anchors */
9959 if (IntGroupLookup(group, cp[k], NULL) == 0) {
9964 return 0; /* Too many bonds */
9967 if (ap->anchor != NULL) {
9968 cp = AtomConnectData(&ap->anchor->connect);
9969 for (k = 0; k < ap->anchor->connect.count; k++) {
9970 if (IntGroupLookup(group, cp[k], NULL) == 0) {
9975 return 0; /* Too many bonds */
9981 if (bond_count == 1) {
9992 /* Returns non-zero if the given group is 'rotatable' in the molecule. The group
9993 is said to be 'rotatable' when either of the following conditions are met; (1)
9994 the group is detachable, or (2) the group consists of two bonded atoms that define
9995 a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
9996 (either a new IntGroup or 'group' with incremented reference count; thus the caller
9997 is responsible for releasing the returned value). */
9999 MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
10002 if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
10003 if (rotGroup != NULL) {
10004 IntGroupRetain(group);
10009 if (group != NULL && IntGroupGetCount(group) == 2) {
10010 i1 = IntGroupGetNthPoint(group, 0);
10011 i2 = IntGroupGetNthPoint(group, 1);
10012 if (MoleculeAreAtomsConnected(mp, i1, i2)) {
10013 IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
10016 i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
10018 IntGroupRelease(frag);
10019 if (rotGroup != NULL)
10023 if (rotGroup != NULL)
10025 else if (frag != NULL)
10026 IntGroupRelease(frag);
10033 #pragma mark ====== Multiple frame ======
10036 MoleculeGetNumberOfFrames(Molecule *mp)
10040 if (mp->nframes <= 0) {
10044 for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10045 if (ap->nframes > n)
10052 return mp->nframes;
10056 MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame, const Vector *inFrameCell)
10058 int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
10059 Vector *tempv, *vp;
10061 if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
10064 n_old = MoleculeGetNumberOfFrames(mp);
10065 n_new = n_old + count;
10066 last_inserted = IntGroupGetNthPoint(group, count - 1);
10067 if (n_new <= last_inserted) {
10068 exframes = last_inserted - n_new + 1; /* number of extra frames that will be silently inserted */
10070 } else exframes = 0;
10072 tempv = (Vector *)malloc(sizeof(Vector) * n_new * 4); /* "*4" for handling cells */
10076 __MoleculeLock(mp);
10078 /* Copy back the current coordinates */
10079 /* No change in the current coordinates, but the frame buffer is updated */
10080 MoleculeSelectFrame(mp, mp->cframe, 1);
10082 /* Expand ap->frames for all atoms */
10083 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10084 if (ap->frames == NULL)
10085 vp = (Vector *)calloc(sizeof(Vector), n_new);
10087 vp = (Vector *)realloc(ap->frames, sizeof(Vector) * n_new);
10089 __MoleculeUnlock(mp);
10092 for (j = ap->nframes; j < n_new; j++)
10096 if (mp->cell != NULL) {
10097 j = mp->nframe_cells;
10098 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, n_new - 1, NULL);
10099 for (i = j; i < n_new; i++) {
10100 /* Set the current cell parameters to the expanded frames */
10101 mp->frame_cells[i * 4] = mp->cell->axes[0];
10102 mp->frame_cells[i * 4 + 1] = mp->cell->axes[1];
10103 mp->frame_cells[i * 4 + 2] = mp->cell->axes[2];
10104 mp->frame_cells[i * 4 + 3] = mp->cell->origin;
10108 /* group = [n0..n1-1, n2..n3-1, ...] */
10110 /* tempv[0..n0-1] <- ap[0..n0-1], s += n0,
10111 tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
10112 tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
10113 tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
10115 tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
10116 At last, s will become n_old and t will become count. */
10117 for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10118 int s, t, ns, ne, mult;
10121 if (i == mp->natoms) {
10122 if (mp->cell == NULL || mp->frame_cells == NULL)
10124 vp = mp->frame_cells;
10131 for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10133 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (ns - ne));
10136 ne = IntGroupGetEndPoint(group, j);
10138 if (i == mp->natoms) {
10139 if (inFrameCell != NULL) {
10140 tempv[ns * 4] = inFrameCell[t * 4];
10141 tempv[ns * 4 + 1] = inFrameCell[t * 4 + 1];
10142 tempv[ns * 4 + 2] = inFrameCell[t * 4 + 2];
10143 tempv[ns * 4 + 3] = inFrameCell[t * 4 + 3];
10145 tempv[ns * 4] = mp->cell->axes[0];
10146 tempv[ns * 4 + 1] = mp->cell->axes[1];
10147 tempv[ns * 4 + 2] = mp->cell->axes[2];
10148 tempv[ns * 4 + 3] = mp->cell->origin;
10151 if (inFrame != NULL)
10152 tempv[ns] = inFrame[natoms * t + i];
10161 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (n_new - ne));
10164 if (i < mp->natoms)
10165 ap->nframes = n_new;
10166 memmove(vp, tempv, sizeof(Vector) * mult * n_new);
10169 mp->nframes = n_new;
10170 MoleculeSelectFrame(mp, last_inserted, 0);
10171 MoleculeIncrementModifyCount(mp);
10172 __MoleculeUnlock(mp);
10177 MoleculeRemoveFrames(Molecule *mp, IntGroup *inGroup, Vector *outFrame, Vector *outFrameCell)
10179 int i, count, n_new, n_old, natoms, nframes, old_count, new_cframe;
10180 Vector *tempv, *vp;
10182 IntGroup *group, *group2;
10184 if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(inGroup)) <= 0)
10187 /* outFrame[] should have enough size for Vector * natoms * group.count */
10188 memset(outFrame, 0, sizeof(Vector) * natoms * count);
10189 if (mp->cell != NULL && mp->frame_cells != NULL)
10190 memset(outFrameCell, 0, sizeof(Vector) * 4 * count);
10192 n_old = MoleculeGetNumberOfFrames(mp);
10194 return -2; /* Cannot delete last frame */
10196 group = IntGroupNew();
10197 group2 = IntGroupNewWithPoints(0, n_old, -1);
10198 IntGroupIntersect(inGroup, group2, group);
10199 IntGroupRelease(group2);
10200 count = IntGroupGetCount(group);
10201 n_new = n_old - count;
10203 IntGroupRelease(group);
10204 return -2; /* Trying to delete too many frames */
10206 tempv = (Vector *)malloc(sizeof(Vector) * n_old * 4); /* "*4" for handling cells */
10207 if (tempv == NULL) {
10208 IntGroupRelease(group);
10212 __MoleculeLock(mp);
10214 /* Copy back the current coordinates */
10215 /* No change in the current coordinates, but the frame buffer is updated */
10216 MoleculeSelectFrame(mp, mp->cframe, 1);
10218 /* Determine which frame should be selected after removal is completed */
10221 if (IntGroupLookup(group, mp->cframe, &i)) {
10222 /* cframe will be removed */
10223 n1 = IntGroupGetStartPoint(group, i) - 1;
10225 n1 = IntGroupGetEndPoint(group, i);
10226 } else n1 = mp->cframe;
10227 /* Change to that frame */
10228 MoleculeSelectFrame(mp, n1, 0);
10229 group2 = IntGroupNewFromIntGroup(group);
10230 IntGroupReverse(group2, 0, n_old);
10231 new_cframe = IntGroupLookupPoint(group2, n1);
10232 if (new_cframe < 0)
10233 return -3; /* This cannot happen */
10234 IntGroupRelease(group2);
10237 /* group = [n0..n1-1, n2..n3-1, ...] */
10239 /* tempv[0..n0-1] -> ap[0..n0-1], s += n0,
10240 tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
10241 tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
10242 tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
10244 tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
10245 At last, s will become n_new and t will become count. */
10247 for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10248 int s, t, j, ns, ne;
10250 /* if i == mp->natoms, mp->frame_cells is handled */
10251 if (i == mp->natoms) {
10252 if (mp->cell == NULL || mp->frame_cells == NULL)
10255 vp = mp->frame_cells;
10261 ap->frames = vp = (Vector *)calloc(sizeof(Vector), n_old);
10263 __MoleculeUnlock(mp);
10267 old_count = ap->nframes;
10270 /* Copy vp to tempv */
10271 memset(tempv, 0, sizeof(Vector) * mult * n_old);
10272 memmove(tempv, vp, sizeof(Vector) * mult * (old_count > n_old ? n_old : old_count));
10273 ne = ns = s = t = 0;
10274 for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10278 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (ns - ne));
10281 ne = IntGroupGetEndPoint(group, j);
10285 if (i < mp->natoms)
10286 outFrame[natoms * t + i] = tempv[ns];
10287 else if (outFrameCell != NULL) {
10288 outFrameCell[t * 4] = tempv[ns * 4];
10289 outFrameCell[t * 4 + 1] = tempv[ns * 4 + 1];
10290 outFrameCell[t * 4 + 2] = tempv[ns * 4 + 2];
10291 outFrameCell[t * 4 + 3] = tempv[ns * 4 + 3];
10298 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (n_old - ne));
10301 if (i < mp->natoms)
10306 if (i < mp->natoms) {
10311 free(mp->frame_cells);
10312 mp->frame_cells = NULL;
10313 mp->nframe_cells = 0;
10316 if (i < mp->natoms)
10317 ap->frames = (Vector *)realloc(ap->frames, sizeof(Vector) * s);
10319 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, s - 1, NULL);
10320 mp->nframe_cells = s;
10325 mp->nframes = nframes;
10327 /* Select the "last" frame; do not "copy back" the coordinates to the frame table */
10328 /* i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe); */
10329 MoleculeSelectFrame(mp, new_cframe, 0);
10331 IntGroupRelease(group);
10333 MoleculeIncrementModifyCount(mp);
10334 __MoleculeUnlock(mp);
10339 MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
10341 int i, cframe, nframes, modified;
10343 cframe = mp->cframe;
10344 nframes = MoleculeGetNumberOfFrames(mp);
10346 frame = mp->cframe;
10347 if (mp == NULL || mp->natoms == 0 || frame < 0 || frame >= nframes)
10350 __MoleculeLock(mp);
10351 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10352 if (copyback && cframe >= 0 && cframe < ap->nframes) {
10353 /* Write the current coordinate back to the frame array */
10354 ap->frames[cframe] = ap->r;
10356 if (frame != cframe && frame >= 0 && frame < ap->nframes) {
10357 /* Read the coordinate from the frame array */
10358 ap->r = ap->frames[frame];
10363 if (mp->cell != NULL && mp->frame_cells != NULL) {
10364 /* Write the current cell back to the frame_cells array */
10365 if (copyback && cframe >= 0) {
10366 Vector *vp = (Vector *)AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, cframe, NULL);
10367 vp[0] = mp->cell->axes[0];
10368 vp[1] = mp->cell->axes[1];
10369 vp[2] = mp->cell->axes[2];
10370 vp[3] = mp->cell->origin;
10372 /* Set the cell from the frame array */
10373 if (frame != cframe && frame >= 0 && frame < mp->nframe_cells) {
10374 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);
10376 MoleculeAmendBySymmetry(mp, NULL, NULL, NULL);
10379 mp->cframe = frame;
10381 mp->needsMDCopyCoordinates = 1;
10382 __MoleculeUnlock(mp);
10383 sMoleculeNotifyChangeAppearance(mp);
10387 /* If molecule is multi-frame, then flush the current information to the frame buffer.
10388 Returns the number of frames. */
10390 MoleculeFlushFrames(Molecule *mp)
10392 int nframes = MoleculeGetNumberOfFrames(mp);
10394 MoleculeSelectFrame(mp, mp->cframe, 1);
10398 #pragma mark ====== Pi Atoms ======
10401 sMoleculeCalculatePiAnchorPosition(Atom *ap, Atom *atoms)
10405 cp = AtomConnectData(&ap->anchor->connect);
10406 n = ap->anchor->connect.count;
10408 for (j = 0; j < n; j++) {
10409 Double w = ap->anchor->coeffs[j];
10410 ap2 = ATOM_AT_INDEX(atoms, cp[j]);
10411 VecScaleInc(ap->r, ap2->r, w);
10416 MoleculeUpdatePiAnchorPositions(Molecule *mol)
10420 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
10421 if (ap->anchor == NULL)
10423 sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
10428 MoleculeCalculatePiAnchorPosition(Molecule *mol, int idx)
10431 if (mol == NULL || idx < 0 || idx >= mol->natoms)
10433 ap = ATOM_AT_INDEX(mol->atoms, idx);
10434 if (ap->anchor == NULL)
10436 sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
10440 MoleculeSetPiAnchorList(Molecule *mol, Int idx, Int nentries, Int *entries, Double *weights, Int *nUndoActions, struct MolAction ***undoActions)
10443 Int *ip, i, j, n, *np;
10445 if (mol == NULL || idx < 0 || idx >= mol->natoms || nentries <= 1)
10446 return -1; /* Invalid argument */
10447 if (weights != NULL) {
10449 for (i = 0; i < nentries; i++) {
10450 if (weights[i] <= 0.0) {
10451 return 10; /* Weights must be positive */
10456 } else d = 1.0 / nentries;
10457 ap = ATOM_AT_INDEX(mol->atoms, idx);
10458 if (ap->anchor != NULL) {
10459 /* Already an anchor: check if bonds/angles/dihedrals have entries related to this anchor */
10460 IntGroup *bg, *ag, *dg, *ig;
10461 Int *ibuf, ibufsize;
10463 bg = ag = dg = ig = NULL;
10464 ip = AtomConnectData(&ap->anchor->connect);
10465 for (i = 0; i < ap->anchor->connect.count; i++) {
10467 for (j = 0; j < nentries; j++) {
10468 if (n == entries[j])
10471 if (j == nentries) {
10472 /* This entry will disappear: if any bond/angle/dihedral has idx-n pair, that should be removed. */
10473 for (j = 0, np = mol->bonds; j < mol->nbonds; j++, np += 2) {
10474 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[0])) {
10476 bg = IntGroupNew();
10477 IntGroupAdd(bg, j, 1);
10480 for (j = 0, np = mol->angles; j < mol->nangles; j++, np += 3) {
10481 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) ||
10482 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1])) {
10484 ag = IntGroupNew();
10485 IntGroupAdd(ag, j, 1);
10488 for (j = 0, np = mol->dihedrals; j < mol->ndihedrals; j++, np += 4) {
10489 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) || (idx == np[2] && n == np[3]) ||
10490 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[3] && n == np[2])) {
10492 dg = IntGroupNew();
10493 IntGroupAdd(dg, j, 1);
10496 for (j = 0, np = mol->impropers; j < mol->nimpropers; j++, np += 4) {
10497 if ((idx == np[0] && n == np[2]) || (idx == np[1] && n == np[2]) || (idx == np[3] && n == np[2]) ||
10498 (idx == np[2] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[2] && n == np[3])) {
10500 ig = IntGroupNew();
10501 IntGroupAdd(ig, j, 1);
10509 /* Delete impropers (with undo info) */
10510 i = IntGroupGetCount(ig);
10511 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
10512 MoleculeDeleteImpropers(mol, ibuf, ig);
10513 if (nUndoActions != NULL && undoActions != NULL) {
10514 act = MolActionNew(gMolActionAddImpropers, i * 4, ibuf, ig);
10515 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10517 IntGroupRelease(ig);
10520 /* Delete dihedrals (with undo info) */
10521 i = IntGroupGetCount(dg);
10522 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
10523 MoleculeDeleteDihedrals(mol, ibuf, dg);
10524 if (nUndoActions != NULL && undoActions != NULL) {
10525 act = MolActionNew(gMolActionAddDihedrals, i * 4, ibuf, dg);
10526 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10528 IntGroupRelease(dg);
10531 /* Delete angles (with undo info) */
10532 i = IntGroupGetCount(ag);
10533 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 3 - 1, NULL);
10534 MoleculeDeleteAngles(mol, ibuf, ag);
10535 if (nUndoActions != NULL && undoActions != NULL) {
10536 act = MolActionNew(gMolActionAddAngles, i * 3, ibuf, ag);
10537 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10539 IntGroupRelease(ag);
10542 /* Delete bonds (with undo info) */
10543 i = IntGroupGetCount(bg);
10544 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 2 - 1, NULL);
10545 MoleculeDeleteBonds(mol, ibuf, bg, NULL, NULL);
10546 if (nUndoActions != NULL && undoActions != NULL) {
10547 act = MolActionNew(gMolActionAddBondsForUndo, i * 2, ibuf, bg);
10548 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10550 IntGroupRelease(bg);
10553 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
10555 AtomConnectResize(&ap->anchor->connect, nentries);
10556 memmove(AtomConnectData(&ap->anchor->connect), entries, sizeof(Int) * nentries);
10557 AssignArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), nentries - 1, NULL);
10558 if (weights != NULL) {
10559 memmove(ap->anchor->coeffs, weights, sizeof(Double) * nentries);
10560 for (i = 0; i < nentries; i++)
10561 ap->anchor->coeffs[i] *= d; /* Normalize weight */
10563 for (i = 0; i < nentries; i++)
10564 ap->anchor->coeffs[i] = d;
10566 MoleculeCalculatePiAnchorPosition(mol, idx);
10570 #pragma mark ====== MO calculation ======
10572 /* Calculate an MO value for a single point. */
10573 /* Index is the MO number (1-based) */
10574 /* tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom. */
10576 sCalcMOPoint(Molecule *mp, const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
10580 Double val, tval, *cnp, *tmpp, *mobasep, *mop;
10582 /* Cache dr and |dr|^2 */
10583 for (i = 0; i < mp->natoms; i++) {
10585 r = ATOM_AT_INDEX(mp->atoms, i)->r;
10586 tmp[i * 4] = r.x = (vp->x - r.x) * kAngstrom2Bohr;
10587 tmp[i * 4 + 1] = r.y = (vp->y - r.y) * kAngstrom2Bohr;
10588 tmp[i * 4 + 2] = r.z = (vp->z - r.z) * kAngstrom2Bohr;
10589 tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
10591 /* Iterate over all shells */
10593 mobasep = bset->mo + (index - 1) * bset->ncomps;
10594 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
10595 pp = bset->priminfos + sp->p_idx;
10596 cnp = bset->cns + sp->cn_idx;
10597 if (sp->a_idx >= mp->natoms)
10598 return 0.0; /* This may happen when molecule is edited after setting up MO info */
10599 tmpp = tmp + sp->a_idx * 4;
10600 mop = mobasep + sp->m_idx;
10604 for (j = 0; j < sp->nprim; j++) {
10605 tval += *cnp++ * exp(-pp->A * tmpp[3]);
10608 val += mop[0] * tval;
10614 for (j = 0; j < sp->nprim; j++) {
10615 tval = exp(-pp->A * tmpp[3]);
10616 x += *cnp++ * tval;
10617 y += *cnp++ * tval;
10618 z += *cnp++ * tval;
10621 x *= mop[0] * tmpp[0];
10622 y *= mop[1] * tmpp[1];
10623 z *= mop[2] * tmpp[2];
10627 case kGTOType_SP: {
10630 for (j = 0; j < sp->nprim; j++) {
10631 tval = exp(-pp->A * tmpp[3]);
10632 t += *cnp++ * tval;
10633 x += *cnp++ * tval;
10634 y += *cnp++ * tval;
10635 z += *cnp++ * tval;
10639 x *= mop[1] * tmpp[0];
10640 y *= mop[2] * tmpp[1];
10641 z *= mop[3] * tmpp[2];
10642 val += t + x + y + z;
10646 Double xx, yy, zz, xy, xz, yz;
10647 xx = yy = zz = xy = xz = yz = 0;
10648 for (j = 0; j < sp->nprim; j++) {
10649 tval = exp(-pp->A * tmpp[3]);
10650 xx += *cnp++ * tval;
10651 yy += *cnp++ * tval;
10652 zz += *cnp++ * tval;
10653 xy += *cnp++ * tval;
10654 xz += *cnp++ * tval;
10655 yz += *cnp++ * tval;
10658 xx *= mop[0] * tmpp[0] * tmpp[0];
10659 yy *= mop[1] * tmpp[1] * tmpp[1];
10660 zz *= mop[2] * tmpp[2] * tmpp[2];
10661 xy *= mop[3] * tmpp[0] * tmpp[1];
10662 xz *= mop[4] * tmpp[0] * tmpp[2];
10663 yz *= mop[5] * tmpp[1] * tmpp[2];
10664 val += xx + yy + zz + xy + xz + yz;
10667 case kGTOType_D5: {
10668 Double d0, d1p, d1n, d2p, d2n;
10669 d0 = d1p = d1n = d2p = d2n = 0;
10670 for (j = 0; j < sp->nprim; j++) {
10671 tval = exp(-pp->A * tmpp[3]);
10672 d0 += *cnp++ * tval;
10673 d1p += *cnp++ * tval;
10674 d1n += *cnp++ * tval;
10675 d2p += *cnp++ * tval;
10676 d2n += *cnp++ * tval;
10679 d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
10680 d1p *= mop[1] * tmpp[0] * tmpp[2];
10681 d1n *= mop[2] * tmpp[1] * tmpp[2];
10682 d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
10683 d2n *= mop[4] * tmpp[0] * tmpp[1];
10684 val += d0 + d1p + d1n + d2p + d2n;
10687 /* TODO: Support F/F7 and G/G9 type orbitals */
10693 /* Calculate one MO. The input vectors are angstrom unit (changed from bohr unit: 20140520) */
10694 /* mono is the MO number (1-based) */
10696 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)
10698 int ix, iy, iz, n, nn;
10701 if (mp == NULL || mp->bset == NULL)
10703 if (mp->bset->cns == NULL) {
10704 if (sSetupGaussianCoefficients(mp->bset) != 0)
10707 if (mp->bset->natoms_bs > mp->natoms)
10708 return -3; /* Number of atoms is smaller than expected (internal error) */
10710 cp = (Cube *)calloc(sizeof(Cube), 1);
10714 cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
10715 if (cp->dp == NULL) {
10728 /* TODO: use multithread */
10729 tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms_bs * 4);
10736 for (ix = 0; ix < nx; ix++) {
10738 for (iy = 0; iy < ny; iy++) {
10739 for (iz = 0; iz < nz; iz++) {
10740 p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
10741 p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
10742 p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
10743 cp->dp[n++] = sCalcMOPoint(mp, mp->bset, mono, &p, tmp);
10745 if (callback != NULL && n - nn > 100) {
10747 if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
10751 return -2; /* User interrupt */
10758 AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
10759 return mp->bset->ncubes - 1;
10762 /* Output values are in angstrom unit (changed from bohr unit: 20140520) */
10764 MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
10767 Vector rmin, rmax, r;
10768 Double dr, dx, dy, dz;
10770 if (mp == NULL || mp->bset == NULL)
10774 rmin.x = rmin.y = rmin.z = 1e10;
10775 rmax.x = rmax.y = rmax.z = -1e10;
10776 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10777 dr = RadiusForAtomicNumber(ap->atomicNumber);
10781 dr = dr * 3.0 + 2.0;
10782 if (rmin.x > r.x - dr)
10784 if (rmin.y > r.y - dr)
10786 if (rmin.z > r.z - dr)
10788 if (rmax.x < r.x + dr)
10790 if (rmax.y < r.y + dr)
10792 if (rmax.z < r.z + dr)
10795 dx = rmax.x - rmin.x;
10796 dy = rmax.y - rmin.y;
10797 dz = rmax.z - rmin.z;
10798 dr = pow(dx * dy * dz / npoints, 1.0/3.0);
10799 *nx = floor(dx / dr + 0.5);
10800 *ny = floor(dy / dr + 0.5);
10801 *nz = floor(dz / dr + 0.5);
10809 xp->x = yp->y = zp->z = dr;
10810 xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
10815 MoleculeGetCubeAtIndex(Molecule *mp, Int index)
10817 if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
10819 return mp->bset->cubes[index];
10823 MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
10826 if (mp == NULL || mp->bset == NULL)
10828 for (i = 0; i < mp->bset->ncubes; i++) {
10829 if (mp->bset->cubes[i]->idn == mono)
10836 MoleculeClearCubeAtIndex(Molecule *mp, Int index)
10839 if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
10841 CubeRelease(mp->bset->cubes[index]);
10843 memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
10844 if (--(mp->bset->ncubes) == 0) {
10845 free(mp->bset->cubes);
10846 mp->bset->cubes = NULL;
10848 return mp->bset->ncubes;
10852 MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
10857 if (mp == NULL || mp->bset == NULL)
10858 return -1; /* Molecule or the basis set information is empty */
10859 cp = MoleculeGetCubeAtIndex(mp, index);
10861 return -2; /* MO not yet calculated */
10862 fp = fopen(fname, "wb");
10864 return -3; /* Cannot create file */
10866 /* Comment lines */
10867 fprintf(fp, "%s MO=%d\n", comment, cp->idn);
10868 fprintf(fp, " MO coefficients\n");
10870 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms_bs),
10871 cp->origin.x * kAngstrom2Bohr, cp->origin.y * kAngstrom2Bohr, cp->origin.z * kAngstrom2Bohr);
10872 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx,
10873 cp->dx.x * kAngstrom2Bohr, cp->dx.y * kAngstrom2Bohr, cp->dx.z * kAngstrom2Bohr);
10874 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny,
10875 cp->dy.x * kAngstrom2Bohr, cp->dy.y * kAngstrom2Bohr, cp->dy.z * kAngstrom2Bohr);
10876 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz,
10877 cp->dz.x * kAngstrom2Bohr, cp->dz.y * kAngstrom2Bohr, cp->dz.z * kAngstrom2Bohr);
10879 /* Atomic information */
10880 for (i = 0; i < mp->natoms; i++) {
10881 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
10882 /* The second number should actually be the effective charge */
10883 fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber,
10884 ap->r.x * kAngstrom2Bohr, ap->r.y * kAngstrom2Bohr, ap->r.z * kAngstrom2Bohr);
10886 fprintf(fp, "%5d%5d\n", 1, 1);
10889 for (i = n = 0; i < cp->nx; i++) {
10890 for (j = 0; j < cp->ny; j++) {
10891 for (k = 0; k < cp->nz; k++) {
10892 /* On Windows, the "%e" format writes the exponent in 3 digits, but
10893 this is not standard. So we avoid using %e */
10894 Double d = cp->dp[n++];
10895 int exponent = (int)floor(log10(fabs(d)));
10896 Double base = d * pow(10, -1.0 * exponent);
10897 fprintf(fp, " %8.5fe%+03d", base, exponent);
10898 /* fprintf(fp, " %12.5e", d); */
10899 if (k == cp->nz - 1 || k % 6 == 5)
10908 #pragma mark ====== Marching Cube (for isosurface) ======
10911 MoleculeClearMCube(Molecule *mol, Int nx, Int ny, Int nz, const Vector *origin, Double dx, Double dy, Double dz)
10913 MCube *mc = mol->mcube;
10915 float rgba[8] = { 1, 1, 1, 0.6, 0, 0, 1, 0.6 };
10920 free(mc->c[0].cubepoints);
10921 free(mc->c[0].triangles);
10923 free(mc->c[1].cubepoints);
10924 free(mc->c[1].triangles);
10925 memmove(rgba, mc->c[0].rgba, sizeof(float) * 4);
10926 memmove(rgba + 4, mc->c[1].rgba, sizeof(float) * 4);
10930 if (nx > 0 && ny > 0 && nz > 0) {
10931 mc = (MCube *)calloc(sizeof(MCube), 1);
10932 /* round up to nearest 4N+1 integer */
10936 mc->nx = (nx + 2) / 4 * 4 + 1;
10937 mc->ny = (ny + 2) / 4 * 4 + 1;
10938 mc->nz = (nz + 2) / 4 * 4 + 1;
10939 mc->dx = dx / mc->nx;
10940 mc->dy = dy / mc->ny;
10941 mc->dz = dz / mc->nz;
10942 mc->origin = *origin;
10943 mc->dp = (Double *)malloc(sizeof(Double) * mc->nx * mc->ny * mc->nz);
10944 if (mc->dp == NULL) {
10948 mc->c[0].fp = (unsigned char *)calloc(sizeof(unsigned char), mc->nx * mc->ny * mc->nz);
10949 mc->c[1].fp = (unsigned char *)calloc(sizeof(unsigned char), mc->nx * mc->ny * mc->nz);
10950 if (mc->c[0].fp == NULL || mc->c[1].fp == NULL) {
10957 for (i = 0; i < mc->nx * mc->ny * mc->nz; i++) {
10958 mc->dp[i] = DBL_MAX;
10960 memmove(mc->c[0].rgba, rgba, sizeof(float) * 4);
10961 memmove(mc->c[1].rgba, rgba + 4, sizeof(float) * 4);
10964 MoleculeCallback_notifyModification(mol, 0);
10968 static int sMarchingCubeTable[256][16] = {
10969 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10970 {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10971 {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10972 {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10973 {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10974 {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10975 {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10976 {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1},
10977 {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10978 {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10979 {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10980 {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1},
10981 {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10982 {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1},
10983 {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1},
10984 {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10985 {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10986 {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10987 {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10988 {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1},
10989 {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10990 {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1},
10991 {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
10992 {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1},
10993 {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10994 {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1},
10995 {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
10996 {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1},
10997 {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1},
10998 {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1},
10999 {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1},
11000 {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1},
11001 {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11002 {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11003 {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11004 {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1},
11005 {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11006 {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
11007 {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1},
11008 {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1},
11009 {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11010 {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
11011 {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
11012 {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1},
11013 {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1},
11014 {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1},
11015 {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1},
11016 {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1},
11017 {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11018 {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
11019 {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1},
11020 {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11021 {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1},
11022 {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1},
11023 {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1},
11024 {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1},
11025 {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
11026 {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1},
11027 {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1},
11028 {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1},
11029 {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1},
11030 {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1},
11031 {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1},
11032 {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11033 {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11034 {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11035 {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11036 {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
11037 {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11038 {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1},
11039 {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1},
11040 {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1},
11041 {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11042 {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
11043 {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
11044 {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1},
11045 {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1},
11046 {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1},
11047 {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1},
11048 {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1},
11049 {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11050 {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1},
11051 {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
11052 {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1},
11053 {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1},
11054 {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1},
11055 {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1},
11056 {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1},
11057 {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
11058 {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1},
11059 {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1},
11060 {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1},
11061 {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1},
11062 {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1},
11063 {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1},
11064 {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1},
11065 {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11066 {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1},
11067 {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
11068 {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1},
11069 {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1},
11070 {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1},
11071 {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11072 {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1},
11073 {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1},
11074 {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1},
11075 {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1},
11076 {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1},
11077 {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1},
11078 {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1},
11079 {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1},
11080 {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11081 {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1},
11082 {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1},
11083 {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1},
11084 {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1},
11085 {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1},
11086 {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1},
11087 {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1},
11088 {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11089 {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1},
11090 {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1},
11091 {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1},
11092 {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1},
11093 {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1},
11094 {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11095 {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1},
11096 {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11097 {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11098 {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11099 {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11100 {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
11101 {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11102 {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
11103 {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
11104 {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1},
11105 {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11106 {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1},
11107 {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1},
11108 {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1},
11109 {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1},
11110 {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1},
11111 {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1},
11112 {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1},
11113 {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11114 {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1},
11115 {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1},
11116 {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1},
11117 {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1},
11118 {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1},
11119 {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1},
11120 {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1},
11121 {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1},
11122 {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11123 {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1},
11124 {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1},
11125 {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1},
11126 {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1},
11127 {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1},
11128 {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11129 {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11130 {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
11131 {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
11132 {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1},
11133 {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
11134 {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1},
11135 {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1},
11136 {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1},
11137 {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
11138 {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1},
11139 {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1},
11140 {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1},
11141 {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1},
11142 {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1},
11143 {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1},
11144 {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1},
11145 {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1},
11146 {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1},
11147 {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1},
11148 {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1},
11149 {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1},
11150 {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1},
11151 {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1},
11152 {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1},
11153 {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1},
11154 {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1},
11155 {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1},
11156 {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11157 {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1},
11158 {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1},
11159 {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11160 {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11161 {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11162 {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1},
11163 {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1},
11164 {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1},
11165 {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1},
11166 {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1},
11167 {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1},
11168 {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1},
11169 {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1},
11170 {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1},
11171 {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1},
11172 {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1},
11173 {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11174 {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1},
11175 {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1},
11176 {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11177 {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1},
11178 {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1},
11179 {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1},
11180 {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1},
11181 {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1},
11182 {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1},
11183 {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1},
11184 {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11185 {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1},
11186 {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1},
11187 {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1},
11188 {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1},
11189 {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1},
11190 {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11191 {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1},
11192 {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11193 {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1},
11194 {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1},
11195 {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1},
11196 {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1},
11197 {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1},
11198 {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1},
11199 {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1},
11200 {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1},
11201 {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1},
11202 {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1},
11203 {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1},
11204 {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11205 {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1},
11206 {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1},
11207 {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11208 {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11209 {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11210 {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1},
11211 {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1},
11212 {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11213 {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1},
11214 {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1},
11215 {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11216 {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11217 {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1},
11218 {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11219 {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1},
11220 {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11221 {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11222 {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11223 {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11224 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
11227 /* Recalculate the MCube */
11228 /* If idn < 0, then the current grid settings and values are unchanged, and */
11229 /* only the marching cubes are regenerated. */
11231 MoleculeUpdateMCube(Molecule *mol, int idn)
11233 Int flags, retval, step, hstep, sn;
11234 Int n, ix, iy, iz, nx, ny, nz;
11235 Int nn, iix, iiy, iiz;
11236 Int ncubepoints, c1, c2, c3;
11238 Double thres, *tmp, dd;
11244 if (mol == NULL || mol->bset == NULL || mol->mcube == NULL)
11246 if (mol->bset->cns == NULL) {
11247 if (sSetupGaussianCoefficients(mol->bset) != 0)
11250 if (mol->bset->natoms_bs > mol->natoms)
11251 return -1; /* Number of atoms is smaller than expected */
11256 Double *mobasep, *mop, mopmax;
11257 Double xmin, xmax, ymin, ymax, zmin, zmax;
11258 /* Clear mcube values */
11259 for (ix = 0; ix < mc->nx * mc->ny * mc->nz; ix++) {
11260 mc->dp[ix] = DBL_MAX;
11261 mc->c[0].fp[ix] = 0;
11262 mc->c[1].fp[ix] = 0;
11265 /* Estimate the orbital sizes */
11266 mc->radii = (Double *)realloc(mc->radii, sizeof(Double) * mol->natoms);
11267 if (mc->radii == NULL)
11268 return -2; /* Out of memory */
11269 mc->nradii = mol->natoms;
11270 memset(mc->radii, 0, sizeof(Double) * mc->nradii);
11271 mobasep = mol->bset->mo + (mc->idn - 1) * mol->bset->ncomps;
11273 for (ix = 0, sp = mol->bset->shells; ix < mol->bset->nshells; ix++, sp++) {
11274 if (sp->a_idx >= mol->natoms)
11275 continue; /* This may happen when molecule is edited after setting up MO info */
11276 mop = mobasep + sp->m_idx;
11277 for (iy = 0; iy < sp->ncomp; iy++) {
11278 dd = fabs(mop[iy]);
11279 if (dd > mc->radii[sp->a_idx])
11280 mc->radii[sp->a_idx] = dd;
11285 xmin = ymin = zmin = 1e10;
11286 xmax = ymax = zmax = -1e10;
11287 for (ix = 0, ap = mol->atoms; ix < mol->natoms; ix++, ap = ATOM_NEXT(ap)) {
11288 dd = RadiusForAtomicNumber(ap->atomicNumber);
11289 dd = (dd * 2.0 + 1.0) * (mc->radii[ix] / mopmax) * (mc->expand > 0.0 ? mc->expand : 1.0);
11290 mc->radii[ix] = dd;
11293 if (p.x - dd < xmin)
11295 if (p.y - dd < ymin)
11297 if (p.z - dd < zmin)
11299 if (p.x + dd > xmax)
11301 if (p.y + dd > ymax)
11303 if (p.z + dd > zmax)
11306 mc->origin.x = xmin;
11307 mc->origin.y = ymin;
11308 mc->origin.z = zmin;
11309 mc->dx = (xmax - xmin) / mc->nx;
11310 mc->dy = (ymax - ymin) / mc->ny;
11311 mc->dz = (zmax - zmin) / mc->nz;
11314 /* Temporary work area */
11315 tmp = (Double *)calloc(sizeof(Double), mol->bset->natoms_bs * 4);
11319 /* TODO: use multithread */
11326 /* Calculate points within certain distances from atoms */
11327 for (nn = 0, ap = mol->atoms; nn < mol->natoms; nn++, ap = ATOM_NEXT(ap)) {
11328 /* dd = RadiusForAtomicNumber(ap->atomicNumber);
11331 dd = dd * 1.5 + 1.0; */
11332 dd = mc->radii[nn];
11333 p.x = ap->r.x - dd - mc->origin.x;
11334 p.y = ap->r.y - dd - mc->origin.y;
11335 p.z = ap->r.z - dd - mc->origin.z;
11339 iix = c1 + ceil(dd * 2.0 / mc->dx);
11340 iiy = c2 + ceil(dd * 2.0 / mc->dy);
11341 iiz = c3 + ceil(dd * 2.0 / mc->dz);
11354 for (ix = c1; ix <= iix; ix++) {
11355 p.x = mc->origin.x + mc->dx * ix;
11356 for (iy = c2; iy <= iiy; iy++) {
11357 p.y = mc->origin.y + mc->dy * iy;
11358 for (iz = c3; iz <= iiz; iz++) {
11359 n = (ix * ny + iy) * nz + iz;
11360 if (mc->dp[n] == DBL_MAX) {
11361 p.z = mc->origin.z + mc->dz * iz;
11362 mc->dp[n] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
11370 /* (i * step, j * step, k * step) */
11371 for (ix = 0; ix < nx; ix += step) {
11372 for (iy = 0; iy < ny; iy += step) {
11373 for (iz = 0; iz < nz; iz += step) {
11374 n = (ix * ny + iy) * nz + iz;
11375 if (mc->dp[n] == DBL_MAX) {
11376 p.x = mc->origin.x + mc->dx * ix;
11377 p.y = mc->origin.y + mc->dy * iy;
11378 p.z = mc->origin.z + mc->dz * iz;
11379 mc->dp[n] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
11386 /* Intermediate points */
11387 for (step = 4; step > 1; step /= 2) {
11389 for (sn = 0; sn <= 1; sn++) {
11391 for (ix = 0; ix < nx - 1; ix += step) {
11392 for (iy = 0; iy < ny - 1; iy += step) {
11393 for (iz = 0; iz < nz - 1; iz += step) {
11395 thres = mc->thres * (sn == 0 ? 1 : -1);
11396 n = (ix * ny + iy) * nz + iz;
11397 if (mc->dp[n] == DBL_MAX || mc->dp[n + step * (nz * (ny + 1) + 1)] == DBL_MAX)
11400 if (mc->dp[n] >= thres)
11402 /* (ix + step, iy, iz) */
11403 if (mc->dp[n + step * ny * nz] >= thres)
11405 /* (ix, iy + step, iz) */
11406 if (mc->dp[n + step * nz] >= thres)
11408 /* (ix + 4, iy + step, iz) */
11409 if (mc->dp[n + step * nz * (ny + 1)] >= thres)
11411 /* (ix, iy, iz + step) */
11412 if (mc->dp[n + step] >= thres)
11414 if (mc->dp[n + step * (ny * nz + 1)] >= thres)
11416 /* (ix, iy + step, iz + step) */
11417 if (mc->dp[n + step * (nz + 1)] >= thres)
11419 /* (ix + step, iy + step, iz + step) */
11420 if (mc->dp[n + step * (nz * (ny + 1) + 1)] >= thres)
11422 if (flags != 0 && flags != 255) {
11423 /* Calc the intermediate points */
11424 for (iix = 0; iix <= step; iix += hstep) {
11425 for (iiy = 0; iiy <= step; iiy += hstep) {
11426 for (iiz = 0; iiz <= step; iiz += hstep) {
11427 if (iix % step == 0 && iiy % step == 0 && iiz % step == 0)
11429 nn = n + (iix * ny + iiy) * nz + iiz;
11430 if (mc->dp[nn] == DBL_MAX) {
11431 p.x = mc->origin.x + mc->dx * (ix + iix);
11432 p.y = mc->origin.y + mc->dy * (iy + iiy);
11433 p.z = mc->origin.z + mc->dz * (iz + iiz);
11434 mc->dp[nn] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
11450 /* Calculate vertex positions and normal vectors */
11451 for (sn = 0; sn <= 1; sn++) {
11453 thres = mc->thres * (sn == 0 ? 1 : -1);
11455 for (ix = 0; ix < nx - 1; ix++) {
11456 for (iy = 0; iy < ny - 1; iy++) {
11457 for (iz = 0; iz < nz - 1; iz++) {
11459 nn = (ix * ny + iy) * nz + iz;
11461 if (dd0 == DBL_MAX)
11464 dd1 = mc->dp[nn + ny * nz];
11465 if (dd1 != DBL_MAX)
11466 p.x = (dd1 - dd0) / mc->dx;
11467 else if (ix > 0 && (dd1 = mc->dp[nn - ny * nz]) != DBL_MAX)
11468 p.x = (dd0 - dd1) / mc->dx;
11469 else continue; /* Cannot define gradient */
11470 dd1 = mc->dp[nn + nz];
11471 if (dd1 != DBL_MAX)
11472 p.y = (dd1 - dd0) / mc->dy;
11473 else if (iy > 0 && (dd1 = mc->dp[nn - nz]) != DBL_MAX)
11474 p.y = (dd0 - dd1) / mc->dy;
11476 dd1 = mc->dp[nn + 1];
11477 if (dd1 != DBL_MAX)
11478 p.z = (dd1 - dd0) / mc->dz;
11479 else if (iz > 0 && (dd1 = mc->dp[nn - 1]) != DBL_MAX)
11480 p.z = (dd0 - dd1) / mc->dz;
11482 NormalizeVec(&p, &p);
11484 if (n + 3 >= mc->c[sn].ncubepoints) {
11485 /* Expand cubepoints[] array */
11486 mc->c[sn].cubepoints = (MCubePoint *)realloc(mc->c[sn].cubepoints, sizeof(MCubePoint) * (mc->c[sn].ncubepoints + 8192));
11487 if (mc->c[sn].cubepoints == NULL) {
11488 mc->c[sn].ncubepoints = 0;
11492 mc->c[sn].ncubepoints += 8192;
11494 mcp = mc->c[sn].cubepoints + n;
11495 iix = (dd0 >= thres ? 1 : -1);
11496 /* (x, y, z)->(x + 1, y, z) */
11497 dd1 = mc->dp[nn + ny * nz];
11498 if (dd1 != DBL_MAX) {
11499 iiy = (dd1 >= thres ? 1 : -1);
11503 mcp->d = (thres - dd0) / (dd1 - dd0);
11504 mcp->pos[0] = mc->origin.x + mc->dx * (ix + mcp->d);
11505 mcp->pos[1] = mc->origin.y + mc->dy * iy;
11506 mcp->pos[2] = mc->origin.z + mc->dz * iz;
11507 mcp->grad[0] = p.x;
11508 mcp->grad[1] = p.y;
11509 mcp->grad[2] = p.z;
11514 /* (x, y, z)->(x, y + 1, z) */
11515 dd1 = mc->dp[nn + nz];
11516 if (dd1 != DBL_MAX) {
11517 iiy = (dd1 >= thres ? 1 : -1);
11520 mcp->key = nn * 3 + 1;
11521 mcp->d = (thres - dd0) / (dd1 - dd0);
11522 mcp->pos[0] = mc->origin.x + mc->dx * ix;
11523 mcp->pos[1] = mc->origin.y + mc->dy * (iy + mcp->d);
11524 mcp->pos[2] = mc->origin.z + mc->dz * iz;
11525 mcp->grad[0] = p.x;
11526 mcp->grad[1] = p.y;
11527 mcp->grad[2] = p.z;
11532 /* (x, y, z)->(x, y, z + 1) */
11533 dd1 = mc->dp[nn + 1];
11534 if (dd1 != DBL_MAX) {
11535 iiy = (dd1 >= thres ? 1 : -1);
11538 mcp->key = nn * 3 + 2;
11539 mcp->d = (thres - dd0) / (dd1 - dd0);
11540 mcp->pos[0] = mc->origin.x + mc->dx * ix;
11541 mcp->pos[1] = mc->origin.y + mc->dy * iy;
11542 mcp->pos[2] = mc->origin.z + mc->dz * (iz + mcp->d);
11543 mcp->grad[0] = p.x;
11544 mcp->grad[1] = p.y;
11545 mcp->grad[2] = p.z;
11553 if (n < mc->c[sn].ncubepoints)
11554 mc->c[sn].cubepoints[n].key = -1; /* End mark */
11556 if (ncubepoints < 3) {
11557 /* Less than 3 points: no triangles */
11558 if (mc->c[sn].ntriangles > 0)
11559 mc->c[sn].triangles[0] = -1; /* End mark */
11564 /* Create triangle table */
11566 for (ix = 0; ix < nx - 1; ix++) {
11567 for (iy = 0; iy < ny - 1; iy++) {
11568 for (iz = 0; iz < nz - 1; iz++) {
11569 nn = (ix * ny + iy) * nz + iz;
11571 if ((dd = mc->dp[nn]) == DBL_MAX)
11573 else if (dd >= thres)
11575 if ((dd = mc->dp[nn + ny * nz]) == DBL_MAX)
11577 else if (dd >= thres)
11579 if ((dd = mc->dp[nn + ny * nz + nz]) == DBL_MAX)
11581 else if (dd >= thres)
11583 if ((dd = mc->dp[nn + nz]) == DBL_MAX)
11585 else if (dd >= thres)
11587 if ((dd = mc->dp[nn + 1]) == DBL_MAX)
11589 else if (dd >= thres)
11591 if ((dd = mc->dp[nn + ny * nz + 1]) == DBL_MAX)
11593 else if (dd >= thres)
11595 if ((dd = mc->dp[nn + ny * nz + nz + 1]) == DBL_MAX)
11597 else if (dd >= thres)
11599 if ((dd = mc->dp[nn + nz + 1]) == DBL_MAX)
11601 else if (dd >= thres)
11603 for (iiy = 0; iiy < 15; iiy++) {
11604 nn = sMarchingCubeTable[iix][iiy];
11607 /* key index for edges 0-11 */
11609 case 0: iiz = (( ix * ny + iy ) * nz + iz ) * 3; break;
11610 case 1: iiz = (((ix + 1) * ny + iy ) * nz + iz ) * 3 + 1; break;
11611 case 2: iiz = (( ix * ny + iy + 1) * nz + iz ) * 3; break;
11612 case 3: iiz = (( ix * ny + iy ) * nz + iz ) * 3 + 1; break;
11613 case 4: iiz = (( ix * ny + iy ) * nz + iz + 1) * 3; break;
11614 case 5: iiz = (((ix + 1) * ny + iy ) * nz + iz + 1) * 3 + 1; break;
11615 case 6: iiz = (( ix * ny + iy + 1) * nz + iz + 1) * 3; break;
11616 case 7: iiz = (( ix * ny + iy ) * nz + iz + 1) * 3 + 1; break;
11617 case 8: iiz = (( ix * ny + iy ) * nz + iz ) * 3 + 2; break;
11618 case 9: iiz = (((ix + 1) * ny + iy ) * nz + iz ) * 3 + 2; break;
11619 case 10: iiz = (((ix + 1) * ny + iy + 1) * nz + iz ) * 3 + 2; break;
11620 case 11: iiz = (( ix * ny + iy + 1) * nz + iz ) * 3 + 2; break;
11622 /* Skip this triangle */
11623 iiy = (iiy - iiy % 3) + 2;
11627 /* Look for the key index in cubepoints */
11629 c3 = ncubepoints - 1;
11630 mcp = mc->c[sn].cubepoints;
11633 /* c1 is always less than c3 */
11634 if (c1 + 1 == c3) {
11635 /* end of search */
11636 if (mcp[c1].key == iiz) {
11638 } else if (mcp[c3].key == iiz) {
11645 c2 = (c1 + c3) / 2;
11646 w = mcp[c2].key - iiz;
11656 /* Not found: skip this triangle */
11657 iiy = (iiy - iiy % 3) + 2;
11661 if (n + 1 >= mc->c[sn].ntriangles) {
11662 /* Expand triangles[] array */
11663 mc->c[sn].triangles = (Int *)realloc(mc->c[sn].triangles, sizeof(Int) * (mc->c[sn].ntriangles + 8192));
11664 if (mc->c[sn].triangles == NULL) {
11665 mc->c[sn].ntriangles = 0;
11669 mc->c[sn].ntriangles += 8192;
11671 mc->c[sn].triangles[n] = c2;
11677 if (n < mc->c[sn].ntriangles)
11678 mc->c[sn].triangles[n] = -1; /* End mark */
11680 /* Estimate the normal vector */
11681 for (n = 0, ip = mc->c[sn].triangles; ip[n] >= 0; n += 3) {
11683 for (ix = 0; ix < 3; ix++) {
11684 mcp = &(mc->c[sn].cubepoints[ip[n + ix]]);
11685 v[ix].x = mcp->pos[0];
11686 v[ix].y = mcp->pos[1];
11687 v[ix].z = mcp->pos[2];
11689 VecDec(v[2], v[0]);
11690 VecDec(v[1], v[0]);
11691 VecCross(v[0], v[1], v[2]);
11692 NormalizeVec(v, v);
11693 for (ix = 0; ix < 3; ix++) {
11694 mcp = &(mc->c[sn].cubepoints[ip[n + ix]]);
11695 mcp->grad[0] += v[0].x;
11696 mcp->grad[1] += v[0].y;
11697 mcp->grad[2] += v[0].z;
11700 for (n = 0, mcp = mc->c[sn].cubepoints; mcp->key >= 0; mcp++) {
11701 if (mcp->grad[0] != 0.0 || mcp->grad[1] != 0.0 || mcp->grad[2] != 0.0) {
11702 dd = 1.0 / sqrt(mcp->grad[0] * mcp->grad[0] + mcp->grad[1] * mcp->grad[1] + mcp->grad[2] * mcp->grad[2]);
11703 mcp->grad[0] *= dd;
11704 mcp->grad[1] *= dd;
11705 mcp->grad[2] *= dd;
11710 MoleculeCallback_notifyModification(mol, 0);
11714 char *MyAppCallback_getDocumentHomeDir(void);
11718 asprintf(&s, "%s/%s", MyAppCallback_getDocumentHomeDir(), "mcube_log.txt");
11719 fp = fopen(s, "w");
11722 for (n = 0; n < mc->nx * mc->ny * mc->nz; n++) {
11723 if (mc->dp[n] == DBL_MAX)
11725 if (dmax < mc->dp[n])
11727 if (dmin > mc->dp[n])
11734 dmax = 1.001 * dmax;
11735 fprintf(fp, "thres = %g = 100\n", mc->thres);
11736 for (iz = 0; iz < mc->nz; iz++) {
11737 fprintf(fp, "z = %d\n", iz);
11738 for (iy = 0; iy < mc->ny; iy++) {
11739 for (ix = 0; ix < mc->nx; ix++) {
11740 n = (ix * ny + iy) * nz + iz;
11743 fprintf(fp, " XXX ");
11745 dd = dd * 100 / mc->thres;
11748 else if (dd < -999.0)
11750 fprintf(fp, "%4d ", (int)(dd));
11758 for (sn = 0; sn <= 1; sn++) {
11759 for (n = 0; n < mc->c[sn].ncubepoints; n++) {
11760 MCubePoint *mcp = mc->c[sn].cubepoints + n;
11765 iz = nn / 3 % mc->nz;
11766 iy = nn / (3 * mc->nz) % mc->ny;
11767 ix = nn / (3 * mc->nz * mc->ny);
11768 fprintf(fp, "%c%d:[%d,%d,%d,%d] (%g,[%g,%g,%g],[%g,%g,%g])\n", (sn == 0 ? 'p' : 'P'),
11769 n, ix, iy, iz, iix,
11770 mcp->d, mcp->pos[0], mcp->pos[1], mcp->pos[2], mcp->grad[0], mcp->grad[1], mcp->grad[2]);
11772 for (n = 0; n < mc->c[sn].ntriangles; n += 3) {
11773 if (mc->c[sn].triangles[n] < 0)
11775 fprintf(fp, "%c%d:(%d,%d,%d)\n", (sn == 0 ? 't' : 'T'), n / 3,
11776 mc->c[sn].triangles[n], mc->c[sn].triangles[n + 1], mc->c[sn].triangles[n + 2]);