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, 0.0, &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, 0.0, &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, 0.0, &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];
5709 for (i = 1, ap = ATOM_NEXT(mp->atoms); i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5711 Int an = ap->atomicNumber;
5713 if (an >= 0 && an < gCountElementParameters)
5714 rad = gElementParameters[an].radius;
5715 else rad = gElementParameters[6].radius;
5716 MoleculeFindCloseAtoms(mp, &r, rad, limit, &nbonds, &bonds, -i);
5719 newbond[0] = kInvalidIndex;
5721 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5724 if (outNbonds != NULL)
5725 *outNbonds = nbonds;
5726 if (outBonds != NULL)
5731 /* Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information */
5733 MoleculeRebuildTablesFromConnects(Molecule *mp)
5735 int i, j, k, retval;
5742 if (mp->nbonds == 0) {
5743 for (i = 0; i < mp->natoms; i++) {
5744 ap = ATOM_AT_INDEX(mp->atoms, i);
5745 cp = AtomConnectData(&ap->connect);
5746 for (j = 0; j < ap->connect.count; j++) {
5752 /* MoleculeAddBonds() should not be used, because it assumes connects[] and
5753 bonds are already in sync */
5754 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
5755 /* retval = MoleculeAddBonds(mp, 1, ibuf);
5763 if (mp->nangles == 0) {
5764 for (i = 0; i < mp->natoms; i++) {
5765 ap = ATOM_AT_INDEX(mp->atoms, i);
5766 cp = AtomConnectData(&ap->connect);
5767 for (j = 0; j < ap->connect.count; j++) {
5768 for (k = j + 1; k < ap->connect.count; k++) {
5773 retval = MoleculeAddAngles(mp, ibuf, NULL);
5781 /* Find dihedrals */
5782 if (mp->ndihedrals == 0) {
5783 for (i = 0; i < mp->natoms; i++) {
5784 ap = ATOM_AT_INDEX(mp->atoms, i);
5785 cp = AtomConnectData(&ap->connect);
5786 for (j = 0; j < ap->connect.count; j++) {
5793 apjj = ATOM_AT_INDEX(mp->atoms, jj);
5794 cpjj = AtomConnectData(&apjj->connect);
5795 for (k = 0; k < ap->connect.count; k++) {
5799 for (m = 0; m < apjj->connect.count; m++) {
5801 if (mm == i || mm == kk)
5808 retval = MoleculeAddDihedrals(mp, ibuf, NULL);
5817 /* Find impropers */
5818 if (mp->nimpropers == 0) {
5819 for (i = 0; i < mp->natoms; i++) {
5820 int i1, i2, i4, n1, n2, n4;
5821 ap = ATOM_AT_INDEX(mp->atoms, i);
5822 cp = AtomConnectData(&ap->connect);
5823 for (i1 = 0; i1 < ap->connect.count; i1++) {
5825 for (i2 = i1 + 1; i2 < ap->connect.count; i2++) {
5827 for (i4 = i2 + 1; i4 < ap->connect.count; i4++) {
5834 retval = MoleculeAddImpropers(mp, ibuf, NULL);
5843 mp->needsMDRebuild = 1;
5844 __MoleculeUnlock(mp);
5848 __MoleculeUnlock(mp);
5853 MoleculeAreAtomsConnected(Molecule *mol, int idx1, int idx2)
5855 Atom *ap1 = ATOM_AT_INDEX(mol->atoms, idx1);
5856 if (AtomConnectHasEntry(&ap1->connect, idx2))
5858 else if (ap1->anchor != NULL && AtomConnectHasEntry(&(ap1->anchor->connect), idx2))
5863 #pragma mark ====== Atom names ======
5865 /* Look for the n1-th atom in resno-th residue (n1 is 0-based) */
5867 MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
5871 if (mp == NULL || mp->natoms == 0)
5874 for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5875 if (ap->resSeq == resno) {
5882 return lasti; /* max */
5887 MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
5891 n = strtol(s, &p, 0);
5903 if ((p = strchr(s, ':')) != NULL) {
5904 /* Residue is specified */
5906 if ((pp = strchr(s, '.')) != NULL && pp < p) {
5907 /* Residue number is also specified */
5910 *resSeq = strtol(pp + 1, &ppp, 0);
5912 return -2; /* Bad format */
5913 while (isspace(*ppp))
5916 return -2; /* Bad format */
5919 /* Check whether the "residue name" is an integer */
5920 n = strtol(s, &pp, 0);
5922 while (isspace(*pp))
5924 if (*pp == 0 || *pp == ':') {
5927 return -2; /* Bad format */
5935 if (n >= sizeof(resName))
5936 n = sizeof(resName) - 1;
5937 strncpy(resName, s, n);
5945 strncpy(atomName, p, 4);
5950 /* Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer */
5952 MoleculeAtomIndexFromString(Molecule *mp, const char *s)
5959 n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
5960 if (atomName[0] == 0) {
5961 if (n >= mp->natoms)
5962 n = -1; /* Out of range */
5965 for (n = 0; n < mp->natoms; n++) {
5966 Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
5967 if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
5968 && (resSeq < 0 || ap->resSeq == resSeq)
5969 && strncmp(atomName, ap->aname, 4) == 0) {
5973 return -1; /* Not found */
5977 MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
5981 if (mp == NULL || index < 0 || index >= mp->natoms) {
5985 ap = mp->atoms + index;
5986 if (ap->resSeq != 0) {
5987 n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
5991 snprintf(buf, bufsize, "%.4s", ap->aname);
5994 #pragma mark ====== Selection ======
5997 sMoleculeNotifyChangeSelection(Molecule *mp)
5999 /* TODO: Finer control of notification types may be necessary */
6000 MoleculeCallback_notifyModification(mp, 0);
6004 MoleculeSetSelection(Molecule *mp, IntGroup *select)
6009 IntGroupRetain(select);
6010 if (mp->selection != NULL)
6011 IntGroupRelease(mp->selection);
6012 mp->selection = select;
6013 sMoleculeNotifyChangeSelection(mp);
6017 MoleculeGetSelection(Molecule *mp)
6021 else return mp->selection;
6025 MoleculeSelectAtom(Molecule *mp, int n1, int extending)
6027 if (mp->selection == NULL)
6028 mp->selection = IntGroupNew();
6030 IntGroupClear(mp->selection);
6031 IntGroupAdd(mp->selection, n1, 1);
6032 sMoleculeNotifyChangeSelection(mp);
6036 MoleculeUnselectAtom(Molecule *mp, int n1)
6038 if (mp->selection != NULL)
6039 IntGroupRemove(mp->selection, n1, 1);
6040 sMoleculeNotifyChangeSelection(mp);
6044 MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
6046 if (mp->selection == NULL)
6047 mp->selection = IntGroupNew();
6048 IntGroupReverse(mp->selection, n1, 1);
6049 sMoleculeNotifyChangeSelection(mp);
6053 MoleculeIsAtomSelected(Molecule *mp, int n1)
6055 if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
6061 MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
6063 if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
6069 MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
6072 IntGroup *remain, *ig1, *ig2;
6074 remain = IntGroupNewFromIntGroup(remove);
6078 status = IntGroupReverse(remain, 0, mp->natoms);
6080 ig1 = IntGroupNew();
6084 status = IntGroupDifference(selection, remove, ig1);
6087 ig2 = IntGroupNew();
6091 status = IntGroupDeconvolute(ig1, remain, ig2);
6094 IntGroupRelease(remain);
6096 IntGroupRelease(ig1);
6101 IntGroupRelease(ig2);
6106 #pragma mark ====== Atom Equivalence ======
6110 struct sEqList *next;
6111 struct sEqList *link;
6114 static struct sEqList *sListBase = NULL;
6115 static struct sEqList *sListFree = NULL;
6117 static struct sEqList *
6121 if (sListFree != NULL) {
6123 sListFree = lp->next;
6124 lp->i[0] = lp->i[1] = 0;
6128 lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
6129 lp->link = sListBase;
6135 sFreeEqList(struct sEqList *list)
6137 list->next = sListFree;
6142 sDeallocateEqLists(void)
6144 struct sEqList *lp, *lp_link;
6145 for (lp = sListBase; lp != NULL; lp = lp_link) {
6154 sExistInEqList(int i, int idx, struct sEqList *list)
6156 while (list != NULL) {
6157 if (list->i[idx] == i)
6164 static struct sEqList *
6165 sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
6168 struct sEqList *list1, *list2;
6169 Int ii, jj, ni, nj, *cpi, *cpj;
6170 api = ATOM_AT_INDEX(mol->atoms, i);
6171 apj = ATOM_AT_INDEX(mol->atoms, j);
6172 if (api->atomicNumber != apj->atomicNumber)
6174 list1 = sAllocEqList();
6180 if (i == j || (db[i] != NULL && db[i] == db[j]))
6182 cpi = AtomConnectData(&api->connect);
6183 cpj = AtomConnectData(&apj->connect);
6184 for (ni = 0; ni < api->connect.count; ni++) {
6186 if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
6188 if (sExistInEqList(ii, 0, list1))
6191 for (nj = 0; nj < apj->connect.count; nj++) {
6193 if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6195 if (sExistInEqList(jj, 1, list1))
6197 list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
6201 if (list2 == NULL) {
6203 return NULL; /* No equivalent to ii */
6205 list1 = list2; /* ii is OK, try next */
6211 sDBInclude(Int *ip, int i)
6216 for (j = ip[0] - 1; j >= 0; j--) {
6224 MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
6226 Int **db; /* List of equivalents for each atom */
6228 Atom *api, *apj, *apk;
6229 Int *cpi, *cpj, *ibuf, nibuf;
6230 int i, j, k, ii, jj, kk;
6231 if (mol == NULL || mol->natoms == 0)
6233 db = (Int **)calloc(sizeof(Int *), mol->natoms);
6237 /* Find the equivalent univalent atoms */
6238 for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6239 if (api->connect.count < 2)
6241 cpi = AtomConnectData(&api->connect);
6242 for (j = 0; j < api->connect.count; j++) {
6246 if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6248 AssignArray(&ibuf, &nibuf, sizeof(Int), n, &jj);
6250 apj = ATOM_AT_INDEX(mol->atoms, jj);
6251 if (apj->connect.count != 1 || db[jj] != NULL)
6253 cpj = AtomConnectData(&apj->connect);
6254 for (k = j + 1; k < api->connect.count; k++) {
6256 if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
6258 apk = ATOM_AT_INDEX(mol->atoms, kk);
6259 if (apk->connect.count != 1 || db[kk] != NULL)
6261 if (apj->atomicNumber == apk->atomicNumber) {
6262 AssignArray(&ibuf, &nibuf, sizeof(Int), n, &kk);
6267 ip = (Int *)calloc(sizeof(Int), n + 1);
6271 memmove(ip + 1, ibuf, sizeof(Int) * n);
6272 for (k = 0; k < n; k++)
6282 /* Try matching (i,j) pair */
6283 for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6284 if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
6286 for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
6287 struct sEqList *list;
6288 if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
6290 if (api->atomicNumber != apj->atomicNumber)
6291 continue; /* Different elements do not match */
6292 if (db[i] != NULL && db[i] == db[j])
6293 continue; /* Already equivalent */
6294 list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
6296 continue; /* (i,j) do not match */
6297 while (list != NULL) {
6300 if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
6301 /* Merge db[ii] and db[jj] */
6302 k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
6303 ip = (Int *)calloc(sizeof(Int), k + 1);
6305 return NULL; /* Out of memory */
6306 if (db[ii] == NULL) {
6310 memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
6313 if (db[jj] == NULL) {
6316 memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
6325 for (k = 0; k < ip[0]; k++)
6329 printf("(%d,%d) matched: ", ii, jj);
6330 for (k = 0; k < ip[0]; k++) {
6331 printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
6341 /* Record the equivalent atoms with the lowest index for each atom */
6342 result = (Int *)calloc(sizeof(Int), mol->natoms);
6343 for (i = 0; i < mol->natoms; i++)
6345 for (i = 0; i < mol->natoms; i++) {
6346 if (result[i] >= 0 || (ip = db[i]) == NULL)
6349 for (j = 0; j < ip[0]; j++) {
6354 for (j = 0; j < ip[0]; j++) {
6355 result[ip[j + 1]] = k;
6356 db[ip[j + 1]] = NULL;
6360 sDeallocateEqLists();
6364 #pragma mark ====== Symmetry expansion ======
6367 MoleculeGetTransformForSymop(Molecule *mp, Symop symop, Transform *tf, int is_cartesian)
6370 if (mp == NULL || mp->cell == NULL)
6372 if (symop.sym >= mp->nsyms && symop.sym != 0)
6374 memmove(*tf, SYMMETRY_AT_INDEX(mp->syms, symop.sym), sizeof(Transform));
6375 (*tf)[9] += symop.dx;
6376 (*tf)[10] += symop.dy;
6377 (*tf)[11] += symop.dz;
6379 TransformMul(t, *tf, mp->cell->rtr);
6380 TransformMul(*tf, mp->cell->tr, t);
6386 MoleculeGetSymopForTransform(Molecule *mp, const Transform tf, Symop *symop, int is_cartesian)
6390 if (mp == NULL || mp->cell == NULL)
6393 TransformMul(t, tf, mp->cell->tr);
6394 TransformMul(t, mp->cell->rtr, t);
6396 memmove(t, tf, sizeof(Transform));
6398 for (i = 0; i < mp->nsyms || i == 0; i++) {
6399 Transform *tp = &(SYMMETRY_AT_INDEX(mp->syms, i));
6400 for (j = 0; j < 9; j++) {
6401 if (fabs((*tp)[j] - t[j]) > 1e-4)
6405 for (j = 9; j < 12; j++) {
6406 double f1 = t[j] - (*tp)[j];
6407 double f2 = floor(f1 + 0.5);
6408 if (fabs(f1 - f2) > 1e-4)
6418 symop->alive = (SYMOP_ALIVE((*symop)) != 0);
6423 return -3; /* Not found */
6427 MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
6431 if (symop.sym >= mp->nsyms && symop.sym != 0)
6433 if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
6434 TransformVec(vpout, mp->cell->rtr, vpin);
6435 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpout);
6436 vpout->x += symop.dx;
6437 vpout->y += symop.dy;
6438 vpout->z += symop.dz;
6439 TransformVec(vpout, mp->cell->tr, vpout);
6441 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpin);
6442 vpout->x += symop.dx;
6443 vpout->y += symop.dy;
6444 vpout->z += symop.dz;
6449 /* Add expanded atoms. Returns the number of newly created atoms.
6450 If indices is non-NULL, it should be an array of Int with at least
6451 IntGroupGetCount(group) entries, and on return it contains the
6452 indices of the expanded atoms (may be existing atoms if the expanded
6453 atoms are already present)
6454 If allowOverlap is non-zero, then the new atom is created even when the
6455 coordinates coincide with the some other atom (special position) of the
6456 same element; otherwise, such atom will not be created and the existing
6457 atom is returned in indices[]. */
6459 MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group, Int *indices, Int allowOverlap)
6461 int i, n, n0, n1, n2, base, count, *table;
6463 IntGroupIterator iter;
6469 if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
6471 if (symop.sym != 0 && symop.sym >= mp->nsyms)
6474 /* Create atoms, with avoiding duplicates */
6475 n0 = n1 = mp->natoms;
6476 table = (int *)malloc(sizeof(int) * n0);
6479 for (i = 0; i < n0; i++)
6481 IntGroupIteratorInit(group, &iter);
6482 MoleculeGetTransformForSymop(mp, symop, &tr, 0);
6484 for (i = 0; i < count; i++) {
6485 n = IntGroupIteratorNext(&iter);
6486 ap = ATOM_AT_INDEX(mp->atoms, n);
6487 if (SYMOP_ALIVE(ap->symop)) {
6488 /* Calculate the cumulative symop */
6490 MoleculeGetTransformForSymop(mp, ap->symop, &t1, 0);
6491 TransformMul(tr2, tr, t1);
6492 if (MoleculeGetSymopForTransform(mp, tr2, &symop1, 0) != 0) {
6493 if (indices != NULL)
6495 continue; /* Skip this atom */
6503 /* Calculate the expande position */
6504 MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
6506 /* Is this expansion already present? */
6507 for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
6508 /* Symmetry operation and the base atom are the same */
6509 if (ap2->symbase == base && SYMOP_EQUAL(symop1, ap2->symop))
6511 /* Atomic number and the position are the same */
6512 if (ap2->atomicNumber == ap->atomicNumber && allowOverlap == 0) {
6513 VecSub(dr, ap2->r, nr);
6514 if (VecLength2(dr) < 1e-6)
6519 /* If yes, then skip it */
6520 if (indices != NULL)
6524 /* Create a new atom */
6526 AtomDuplicate(&newAtom, ap);
6527 MoleculeCreateAnAtom(mp, &newAtom, -1);
6528 AtomClean(&newAtom);
6529 ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
6531 ap2->symbase = base;
6532 ap2->symop = symop1;
6533 ap2->symop.alive = (symop1.dx != 0 || symop1.dy != 0 || symop1.dz != 0 || symop1.sym != 0);
6534 table[n] = n1; /* The index of the new atom */
6535 MoleculeSetAnisoBySymop(mp, n1); /* Recalculate anisotropic parameters according to symop */
6536 if (indices != NULL)
6541 IntGroupIteratorRelease(&iter);
6544 for (i = n0; i < n1; i++) {
6546 ap = ATOM_AT_INDEX(mp->atoms, i);
6547 if (SYMOP_ALIVE(ap->symop) && MoleculeGetTransformForSymop(mp, ap->symop, &tr, 1) == 0) {
6548 /* For each connected atom, look for the transformed atom */
6550 ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
6551 cp = AtomConnectData(&ap2->connect);
6552 n2 = ap2->connect.count;
6553 for (n = 0; n < n2; n++) {
6554 Atom *apn = ATOM_AT_INDEX(mp->atoms, cp[n]);
6556 TransformVec(&nr, tr, &nr);
6557 /* Look for the bonded atom transformed by ap->symop */
6558 for (j = 0, ap2 = mp->atoms; j < mp->natoms; j++, ap2 = ATOM_NEXT(ap2)) {
6559 if (ap2->symbase == cp[n] && SYMOP_EQUAL(ap->symop, ap2->symop))
6561 VecSub(dr, nr, ap2->r);
6562 if (ap2->atomicNumber == apn->atomicNumber && VecLength2(dr) < 1e-6)
6565 if (j < mp->natoms) {
6566 /* Bond i-j is created */
6569 if (MoleculeLookupBond(mp, b[0], b[1]) < 0)
6570 MoleculeAddBonds(mp, 1, b, NULL, 1);
6575 mp->needsMDRebuild = 1;
6576 __MoleculeUnlock(mp);
6578 return n1 - n0; /* The number of added atoms */
6581 /* Recalculate the coordinates of symmetry expanded atoms.
6582 (Also recalculate the positions of pi-anchor atoms)
6583 Returns the number of affected atoms.
6584 If group is non-NULL, only the expanded atoms whose base atoms are in the
6585 given group are considered.
6586 If groupout and vpout are non-NULL, the indices of the affected atoms
6587 and the original positions are returned (for undo operation).
6588 The pointers returned in *groupout and *vpout must be released and
6589 free()'ed by the caller */
6591 MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
6596 IntGroup *ig = NULL;
6599 if (mp == NULL || mp->natoms == 0)
6604 if (mp->nsyms != 0) {
6605 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6606 if (!SYMOP_ALIVE(ap->symop))
6608 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
6610 bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
6611 MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
6612 VecSub(dr, nr, ap->r);
6613 if (VecLength2(dr) < 1e-20)
6615 if (groupout != NULL) {
6618 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
6621 IntGroupAdd(ig, i, 1);
6627 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6629 if (ap->anchor == NULL)
6631 if (group != NULL) {
6632 if (IntGroupLookup(group, i, NULL) == 0) {
6633 n = ap->anchor->connect.count;
6634 ip = AtomConnectData(&(ap->anchor->connect));
6635 for (j = 0; j < n; j++) {
6636 if (IntGroupLookup(group, ip[j], NULL) != 0)
6640 continue; /* This pi-anchor should not be modified */
6644 MoleculeCalculatePiAnchorPosition(mp, i);
6645 VecSub(dr, nr, ap->r);
6646 if (VecLength2(dr) < 1e-20) {
6647 ap->r = nr; /* No change */
6650 if (groupout != NULL) {
6653 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
6656 IntGroupAdd(ig, i, 1);
6660 mp->needsMDCopyCoordinates = 1;
6661 __MoleculeUnlock(mp);
6664 if (groupout != NULL && vpout != NULL) {
6666 *vpout = (Vector *)realloc(vp, sizeof(Vector) * count);
6668 IntGroupRelease(ig);
6672 if (groupout != NULL && vpout != NULL) {
6680 #pragma mark ====== Show/hide atoms ======
6683 sMoleculeNotifyChangeAppearance(Molecule *mp)
6685 /* TODO: Finer control of notification types may be necessary */
6686 MoleculeCallback_notifyModification(mp, 0);
6691 sMoleculeUnselectHiddenAtoms(Molecule *mp)
6694 if (mp == NULL || mp->selection == NULL)
6696 for (i = 0; i < mp->natoms; i++) {
6697 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6698 if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
6699 IntGroupRemove(mp->selection, i, 1);
6701 sMoleculeNotifyChangeAppearance(mp);
6705 MoleculeShowAllAtoms(Molecule *mp)
6710 for (i = 0; i < mp->natoms; i++) {
6711 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6712 ap->exflags &= ~kAtomHiddenFlag;
6714 sMoleculeNotifyChangeAppearance(mp);
6719 MoleculeShowReverse(Molecule *mp)
6724 for (i = 0; i < mp->natoms; i++) {
6725 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6726 ap->exflags ^= kAtomHiddenFlag;
6728 sMoleculeUnselectHiddenAtoms(mp);
6729 sMoleculeNotifyChangeAppearance(mp);
6734 MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
6737 if (mp == NULL || ig == NULL)
6739 for (i = 0; i < mp->natoms; i++) {
6740 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6741 if (ap->exflags & kAtomHiddenFlag)
6742 continue; /* Already hidden */
6743 if (IntGroupLookupPoint(ig, i) >= 0)
6744 ap->exflags |= kAtomHiddenFlag;
6746 sMoleculeUnselectHiddenAtoms(mp);
6747 sMoleculeNotifyChangeAppearance(mp);
6751 #pragma mark ====== Reversible Editing ======
6755 sMoleculeNotifyModification(Molecule *mp)
6757 ** TODO: Finer control of notification types may be necessary **
6758 MoleculeCallback_notifyModification(mp, 0);
6762 /* Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup */
6764 sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
6767 if (where == NULL) {
6768 /* Append the new objects at the end */
6769 memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
6772 n1 = IntGroupGetCount(where); /* Position to get new object */
6773 n2 = nobjs; /* Position to get old object */
6774 n3 = n1 + n2; /* Position to place new/old object */
6775 for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
6776 int start = IntGroupGetStartPoint(where, i);
6777 int end = IntGroupGetEndPoint(where, i);
6779 /* old[end-(n3-n2)..n2-1] is moved to old[end..n3-1] */
6780 memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
6781 n2 = end - (n3 - n2);
6784 /* new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1] */
6785 memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
6792 /* Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup */
6794 sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6796 int n1, n2, n3, start, end, i;
6797 if (where == NULL || IntGroupGetCount(where) == 0)
6798 return 0; /* No operation */
6799 if (objs == NULL || nobjs == 0)
6800 return 1; /* Bad argument */
6801 n1 = 0; /* Position to move remaining elements to */
6802 n2 = 0; /* Position to move remaining elements from */
6803 n3 = 0; /* Position to move removed elements to */
6804 for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6805 end = IntGroupGetEndPoint(where, i);
6807 /* Move (start - n2) elements from objs[n2] to objs[n1] */
6809 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
6813 /* Move (end - start) elements from objs[n2] to clip[n3] */
6815 memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
6816 n3 += (end - start);
6817 n2 += (end - start);
6819 /* Move (nobjs - n2) elements from objs[n2] to objs[n1] */
6821 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
6825 /* Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup */
6827 sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6829 int n1, start, end, i;
6830 if (objs == NULL || where == NULL)
6831 return 1; /* Bad argument */
6832 n1 = 0; /* Position to move removed elements to */
6833 for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6834 end = IntGroupGetEndPoint(where, i);
6835 /* Copy (end - start) elements from objs[start] to clip[n1] */
6837 memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
6838 n1 += (end - start);
6843 /* Create a new atom with no bonding information. ap must _not_ be inside the given molecule
6844 (Use AtomDuplicate() first) */
6846 MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
6850 if (mp == NULL || ap == NULL || mp->noModifyTopology)
6853 if (pos < 0 || pos >= mp->natoms)
6855 ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
6857 goto error; /* Out of memory */
6858 ap1 = ATOM_AT_INDEX(mp->atoms, pos);
6859 if (pos < mp->natoms - 1) {
6860 memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6862 if (AtomDuplicate(ap1, ap) == NULL) {
6863 /* Cannot duplicate: restore the original state */
6864 memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6868 ap1->connect.count = 0;
6869 if (ap1->resSeq >= mp->nresidues)
6870 AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
6871 if (ap1->resName[0] == 0)
6872 strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
6873 if (ap1->segName[0] == 0)
6874 strncpy(ap1->segName, "MAIN", 4);
6875 if (pos < mp->natoms - 1) {
6876 /* Renumber the connect table, bonds, angles, etc. */
6877 for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
6880 cp = AtomConnectData(&api->connect);
6881 for (j = 0; j < api->connect.count; j++) {
6885 if (api->anchor != NULL) {
6886 cp = AtomConnectData(&api->anchor->connect);
6887 for (j = 0; j < api->anchor->connect.count; j++) {
6893 for (i = 0; i < mp->nbonds * 2; i++) {
6894 if (mp->bonds[i] >= pos)
6897 for (i = 0; i < mp->nangles * 3; i++) {
6898 if (mp->angles[i] >= pos)
6901 for (i = 0; i < mp->ndihedrals * 4; i++) {
6902 if (mp->dihedrals[i] >= pos)
6905 for (i = 0; i < mp->nimpropers * 4; i++) {
6906 if (mp->impropers[i] >= pos)
6910 mp->nframes = -1; /* Should be recalculated later */
6911 MoleculeIncrementModifyCount(mp);
6912 mp->needsMDRebuild = 1;
6913 __MoleculeUnlock(mp);
6916 __MoleculeUnlock(mp);
6922 static int s_error_count;
6925 s_fprintf(FILE *fp, const char *fmt, ...)
6930 return vfprintf(fp, fmt, va);
6934 MoleculeCheckSanity(Molecule *mol)
6936 const char *fail = "Sanity check failure";
6937 Int i, j, *ip, c[4];
6940 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
6941 if (ap->resSeq >= mol->nresidues)
6942 s_fprintf(stderr, "%s: atom %d residue %d but nresidues %d\n", fail, i, ap->resSeq, mol->nresidues);
6943 if (ap->type != 0 && ap->type < kAtomTypeMinimum)
6944 s_fprintf(stderr, "%s: atom %d atom type %d less than minimum\n", fail, i, ap->type);
6945 if (ap->atomicNumber < 0 || ap->atomicNumber > 113)
6946 s_fprintf(stderr, "%s: atom %d atomic number %d\n", fail, i, ap->atomicNumber);
6947 ip = AtomConnectData(&ap->connect);
6948 for (j = 0; j < ap->connect.count; j++) {
6949 if (ip[j] < 0 || ip[j] >= mol->natoms)
6950 s_fprintf(stderr, "%s: atom %d connect[%d] = %d out of range\n", fail, i, j, ip[j]);
6951 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[j])->connect), i) == 0)
6952 s_fprintf(stderr, "%s: atom %d has connect %d but atom %d has no connect %d\n", fail, i, ip[j], ip[j], i);
6955 for (i = 0, ip = mol->bonds; i < mol->nbonds; i++, ip += 2) {
6956 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms)
6957 s_fprintf(stderr, "%s: bond %d %d-%d out of range\n", fail, i, ip[0], ip[1]);
6958 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[0])->connect), ip[1]) == 0)
6959 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]);
6961 for (i = 0, ip = mol->angles; i < mol->nangles; i++, ip += 3) {
6962 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms || ip[2] < 0 || ip[2] >= mol->natoms)
6963 s_fprintf(stderr, "%s: angle %d %d-%d-%d out of range\n", fail, i, ip[0], ip[1], ip[2]);
6964 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
6966 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]);
6967 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
6969 s_fprintf(stderr, "%s: angle %d %d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[1], ip[2]);
6970 if (c[0] == 2 && c[1] == 2)
6971 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]);
6973 for (i = 0, ip = mol->dihedrals; i < mol->ndihedrals; i++, ip += 4) {
6974 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)
6975 s_fprintf(stderr, "%s: dihedral %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
6976 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
6977 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
6978 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
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[0]);
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[1], ip[2]);
6984 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]);
6986 for (i = 0, ip = mol->impropers; i < mol->nimpropers; i++, ip += 4) {
6987 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)
6988 s_fprintf(stderr, "%s: improper %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
6989 c[0] = MoleculeAreAtomsConnected(mol, ip[2], ip[0]);
6990 c[1] = MoleculeAreAtomsConnected(mol, ip[2], ip[1]);
6991 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
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[0]);
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[1]);
6997 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]);
6999 return s_error_count;
7003 /* Merge two molecules. We use this procedure for all add-atom operations. */
7004 /* resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
7005 /* If nactions and actions are non-NULL, then the corresponding undo actions are created and returned. */
7006 /* If forUndo is non-zero, then only the atoms are inserted; other information should be inserted
7007 separately by other undo actions. */
7009 MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, Int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
7012 Int i, j, n1, n2, n3, n4, *cp;
7013 Int *new2old, *old2new;
7018 if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
7019 return 0; /* Do nothing */
7021 if (dst->noModifyTopology)
7022 return 1; /* Prohibited operation */
7024 if (where != NULL && IntGroupGetCount(where) != src->natoms)
7025 return 1; /* Bad parameter */
7027 if (nactions != NULL)
7029 if (actions != NULL)
7033 __MoleculeLock(dst);
7037 if (resSeqOffset < 0)
7040 /* Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
7041 and ndst..ndst+nsrc-1 are for atoms in src. */
7042 new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
7043 if (new2old == NULL)
7045 old2new = new2old + ndst + nsrc;
7046 n1 = 0; /* dst index */
7047 n2 = 0; /* src index */
7048 n3 = 0; /* "merged" index */
7050 while (n1 < ndst || n2 < nsrc) {
7051 if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
7054 /* n4 elements from dst[n1] will go to merged[n3] */
7055 for (j = 0; j < n4; j++) {
7056 old2new[n1 + j] = n3 + j;
7057 new2old[n3 + j] = n1 + j;
7061 if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
7063 /* n4 elements from src[n2] will go to merged[n3] */
7064 for (j = 0; j < n4; j++) {
7065 old2new[ndst + n2 + j] = n3 + j;
7066 new2old[n3 + j] = ndst + n2 + j;
7073 /* Expand the destination array */
7074 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
7077 /* Move the atoms */
7078 if (where == NULL) {
7079 /* Duplicate atoms to the end of the destination array */
7080 for (i = 0; i < nsrc; i++) {
7081 ap = ATOM_AT_INDEX(dst->atoms, ndst + i);
7082 if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7084 if (forUndo) /* For undo action, all bonds come from another undo action, so connection info are cleared */
7085 AtomConnectResize(&ap->connect, 0);
7088 /* Duplicate to a temporary storage and then insert */
7089 Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
7090 if (tempatoms == NULL)
7092 for (i = 0; i < nsrc; i++) {
7093 ap = ATOM_AT_INDEX(tempatoms, i);
7094 if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7096 if (forUndo) /* See above */
7097 AtomConnectResize(&ap->connect, 0);
7099 if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
7103 dst->natoms = ndst + nsrc;
7105 /* Renumber the atom indices in connect[] and symbase, and modify the residue numbers */
7106 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7107 if (new2old[i] < ndst) {
7108 /* This atom is from dst */
7111 /* This atom is from src */
7112 n1 = ndst; /* Offset to the internal number */
7113 if (ap->resSeq != 0)
7114 ap->resSeq += resSeqOffset; /* Modify residue number */
7116 cp = AtomConnectData(&ap->connect);
7117 for (j = 0; j < ap->connect.count; j++)
7118 cp[j] = old2new[cp[j] + n1];
7119 if (SYMOP_ALIVE(ap->symop))
7120 ap->symbase = old2new[ap->symbase + n1];
7121 if (ap->anchor != NULL) {
7122 cp = AtomConnectData(&ap->anchor->connect);
7123 for (j = 0; j < ap->anchor->connect.count; j++)
7124 cp[j] = old2new[cp[j] + n1];
7128 /* Move the bonds, angles, dihedrals, impropers */
7129 for (i = 0; i < 4; i++) {
7130 Int *nitems, *nitems_src;
7131 Int **items, **items_src;
7132 Int nsize; /* Number of Ints in one element */
7135 nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
7137 nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
7139 nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
7141 nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
7143 nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
7144 items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
7146 /* During undo, no bonds etc. are copied from src; they will be taken care later
7151 /* Keep the old number of entries in dst, because it is updated by AssignArray() */
7153 /* Also keep the old number of entries in src, in case src and dst point the same molecule */
7155 /* Expand the array */
7156 if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
7158 /* Copy the items */
7159 memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
7161 /* Copy the bond order info if present */
7162 Int nn1 = dst->nbondOrders;
7163 if (dst->bondOrders != NULL || src->bondOrders != NULL) {
7164 if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), dst->nbonds - 1, NULL) == NULL)
7166 memset(dst->bondOrders + nn1, 0, sizeof(Double) * (dst->nbonds - nn1));
7167 if (src->bondOrders != NULL)
7168 memmove(dst->bondOrders + n1, src->bondOrders, sizeof(Double) * n2);
7173 for (j = 0; j < n1 * nsize; j++)
7174 (*items)[j] = old2new[(*items)[j]];
7175 for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
7176 (*items)[j] = old2new[(*items)[j] + ndst];
7177 if (forUndo == 0 && actions != NULL) {
7178 ig = IntGroupNewWithPoints(n1, n2, -1);
7180 case 0: act = MolActionNew(gMolActionDeleteBonds, ig); break;
7181 case 1: act = MolActionNew(gMolActionDeleteAngles, ig); break;
7182 case 2: act = MolActionNew(gMolActionDeleteDihedrals, ig); break;
7183 case 3: act = MolActionNew(gMolActionDeleteImpropers, ig); break;
7185 IntGroupRelease(ig);
7186 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7191 /* Renumber existing parameters */
7192 if (dst->par != NULL) {
7194 for (type = kFirstParType; type <= kLastParType; type++) {
7196 n1 = ParameterGetCountForType(dst->par, type);
7197 for (i = 0; i < n1; i++) {
7198 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7199 ParameterRenumberAtoms(type, up1, ndst, old2new);
7204 /* Merge parameters from src */
7205 if (src->par != NULL && forUndo == 0) {
7206 UnionPar *up1, *up2;
7208 if (dst->par == NULL)
7209 dst->par = ParameterNew();
7211 /* Renumber existing parameters */
7212 for (type = kFirstParType; type <= kLastParType; type++) {
7213 n1 = ParameterGetCountForType(dst->par, type);
7214 for (i = 0; i < n1; i++) {
7215 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7216 ParameterRenumberAtoms(type, up1, ndst, old2new);
7221 for (type = kFirstParType; type <= kLastParType; type++) {
7222 n1 = ParameterGetCountForType(src->par, type);
7223 n2 = ParameterGetCountForType(dst->par, type);
7226 /* Determine which parameter should be copied from src to dst */
7227 for (i = 0; i < n1; i++) {
7229 up1 = ParameterGetUnionParFromTypeAndIndex(src->par, type, i);
7230 n3 = ParameterGetAtomTypes(type, up1, types);
7231 for (j = 0; j < n3; j++) {
7232 /* If it includes explicit atom index, then it should be copied */
7233 if (types[j] < kAtomTypeMinimum) {
7234 IntGroupAdd(ig, i, 1);
7239 for (j = 0; j < n2; j++) {
7240 up2 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, j);
7241 if (ParameterCompare(up1, up2, type))
7245 /* This is an unknown parameter; should be copied */
7246 IntGroupAdd(ig, i, 1);
7249 n1 = IntGroupGetCount(ig);
7252 up1 = (UnionPar *)calloc(sizeof(UnionPar), n1);
7255 /* Copy parameters and renumber indices if necessary */
7256 for (i = j = 0; i < n1; i++) {
7257 up2 = ParameterGetUnionParFromTypeAndIndex(src->par, type, IntGroupGetNthPoint(ig, i));
7261 ParameterRenumberAtoms(type, up1 + j, nsrc, old2new + ndst);
7264 /* Merge parameters */
7266 IntGroupAdd(ig, n2, j);
7267 if (ParameterInsert(dst->par, type, up1, ig) < j)
7269 if (actions != NULL) {
7270 act = MolActionNew(gMolActionDeleteParameters, type, ig);
7271 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7277 IntGroupRelease(ig);
7280 /* Copy the residues if necessary */
7281 /* src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
7282 However, 1+resSeqOffset should not overwrite the existing residue in dst;
7283 i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1]. */
7285 n1 = dst->nresidues;
7286 if (1 + resSeqOffset < n1) {
7288 } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
7289 if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
7290 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
7292 memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
7293 if (nactions != NULL) {
7294 act = MolActionNew(gMolActionChangeNumberOfResidues, n1);
7295 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7301 MoleculeCleanUpResidueTable(dst);
7304 dst->nframes = -1; /* Should be recalculated later */
7306 MoleculeIncrementModifyCount(dst);
7307 dst->needsMDRebuild = 1;
7308 __MoleculeUnlock(dst);
7312 __MoleculeUnlock(dst);
7313 Panic("Low memory while adding atoms");
7314 return 1; /* Not reached */
7317 /* Unmerge the molecule. If necessary, the undo actions are stored in nactions/actions array.
7318 (The nactions/actions array must be initialized by the caller) */
7320 sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag, Int *nactions, MolAction ***actions, Int forUndo)
7322 Int nsrc, ndst, nsrcnew;
7323 Int i, j, n1, n2, n3, n4, *cp;
7324 Int *new2old, *old2new;
7325 IntGroup *move_g, *del_g, *remain_g, *dst_par_g, *remove_par_g;
7331 if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
7338 if (src->noModifyTopology && moveFlag)
7339 return 1; /* Prohibit editing */
7341 if ((ndst = IntGroupGetCount(where)) > src->natoms)
7342 return 1; /* Bad parameter */
7344 __MoleculeLock(src);
7349 nsrcnew = nsrc - ndst;
7350 if (resSeqOffset < 0)
7353 /* Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
7354 and nsrcnew..nsrc-1 are for atoms moved into dst. */
7355 new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
7356 if (new2old == NULL)
7358 old2new = new2old + nsrc;
7359 n1 = 0; /* src index */
7360 n2 = 0; /* dst index */
7361 n3 = 0; /* src index after "unmerge" */
7363 while (n1 < nsrc || n2 < ndst) {
7364 if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
7367 /* n4 elements from src[n1] will go to unmerged[n3] */
7368 for (j = 0; j < n4; j++) {
7369 old2new[n1 + j] = n3 + j;
7370 new2old[n3 + j] = n1 + j;
7374 if ((n4 = IntGroupGetInterval(where, i)) < 0)
7376 /* n4 elements from src[n1] will go to dst[n2] */
7377 for (j = 0; j < n4; j++) {
7378 old2new[n1 + j] = nsrcnew + n2 + j;
7379 new2old[nsrcnew + n2 + j] = n1 + j;
7386 /* Atoms to remain in the source group */
7388 remain_g = IntGroupNewWithPoints(0, nsrc, -1);
7389 IntGroupRemoveIntGroup(remain_g, where);
7390 } else remain_g = NULL;
7392 /* Find parameters to be moved to the dst (dst_par_g), and to be removed from the src (remove_par_g) */
7393 if (src->par != NULL) {
7394 dst_par_g = IntGroupNew();
7396 remove_par_g = IntGroupNew();
7397 else remove_par_g = NULL;
7398 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7399 n2 = ParameterGetCountForType(src->par, n1);
7402 for (i = 0; i < n2; i++) {
7403 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7404 if (ParameterIsRelevantToAtomGroup(n1, up, src->atoms, where)) {
7405 /* This parameter is to be copied to dst */
7406 IntGroupAdd(dst_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7408 if (moveFlag && !ParameterIsRelevantToAtomGroup(n1, up, src->atoms, remain_g)) {
7409 /* This parameter is to be removed */
7410 IntGroupAdd(remove_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7414 } else dst_par_g = remove_par_g = NULL;
7416 /* Pi anchors should be modified if the anchor and its component atoms become separated between
7419 Int ibufsize, *ibuf, flag_i, flag_j;
7421 ibuf = (Int *)malloc(sizeof(Int) * ibufsize);
7422 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
7423 if (ap->anchor == NULL)
7425 flag_i = (old2new[i] < nsrcnew);
7426 cp = AtomConnectData(&ap->anchor->connect);
7427 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7428 flag_j = (old2new[cp[j]] < nsrcnew);
7429 if (flag_i == flag_j) {
7430 if (n1 >= ibufsize) {
7432 ibuf = (Int *)realloc(ibuf, sizeof(Int) * ibufsize);
7438 /* Need to modify the pi anchor list */
7441 MolActionCreateAndPerform(src, SCRIPT_ACTION("isI"), "set_atom_attr", i, "anchor_list", n1, ibuf);
7446 /* Make a new molecule */
7448 dst = MoleculeNew();
7451 /* Expand the destination array */
7452 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
7454 dst_ap = dst->atoms;
7457 dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
7462 /* Move the atoms */
7464 if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
7466 src->natoms = nsrcnew;
7468 /* The atom record must be deallocated correctly */
7469 for (i = 0; i < ndst; i++)
7470 AtomClean(ATOM_AT_INDEX(dst_ap, i));
7474 for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
7475 AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
7480 /* The dummy destination array is no longer needed */
7485 /* Renumber the atom indices in connect[] (src) */
7487 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
7488 cp = AtomConnectData(&ap->connect);
7489 for (j = n1 = 0; j < ap->connect.count; j++) {
7490 n2 = old2new[cp[j]];
7494 AtomConnectResize(&ap->connect, n1);
7495 if (ap->anchor != NULL) {
7496 cp = AtomConnectData(&ap->anchor->connect);
7497 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7498 n2 = old2new[cp[j]];
7502 if (n1 != ap->anchor->connect.count) {
7503 /* This should not happen!! */
7504 AtomConnectResize(&ap->anchor->connect, n1);
7505 fprintf(stderr, "Internal error in sMoleculeUnmergeSub (line %d)\n", __LINE__);
7507 free(ap->anchor->coeffs);
7516 /* Renumber the atom indices in connect[] (dst) */
7518 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7519 if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
7520 ap->resSeq -= resSeqOffset;
7521 else ap->resSeq = 0;
7522 cp = AtomConnectData(&ap->connect);
7523 for (j = n1 = 0; j < ap->connect.count; j++) {
7524 n2 = old2new[cp[j]] - nsrcnew;
7528 AtomConnectResize(&ap->connect, n1);
7529 if (ap->anchor != NULL) {
7530 cp = AtomConnectData(&ap->anchor->connect);
7531 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7532 n2 = old2new[cp[j]] - nsrcnew;
7536 if (n1 != ap->anchor->connect.count) {
7537 /* This can happen, and the anchor info is silently modified */
7539 AtomConnectResize(&ap->anchor->connect, 0);
7540 free(ap->anchor->coeffs);
7545 AtomConnectResize(&ap->anchor->connect, n1);
7547 for (j = 0; j < n1; j++)
7548 d += ap->anchor->coeffs[j];
7549 for (j = 0; j < n1; j++)
7550 ap->anchor->coeffs[j] /= d;
7551 MoleculeCalculatePiAnchorPosition(dst, i);
7558 /* Separate the bonds, angles, dihedrals, impropers */
7559 /* TODO: Improper torsions should also be copied! */
7560 move_g = IntGroupNew();
7563 for (i = 3; i >= 0; i--) {
7564 Int *nitems, *nitems_dst;
7565 Int **items, **items_dst;
7566 Int nsize; /* Number of Ints in one element */
7567 unsigned char *counts;
7568 del_g = IntGroupNew();
7571 nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
7573 nitems = &src->nangles; items = &src->angles; nsize = 3; break;
7575 nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
7577 nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
7579 nitems = NULL; items = NULL; nsize = 0; break; /* Not reached */
7582 nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
7583 items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
7588 counts = (unsigned char *)calloc(1, *nitems);
7589 /* Find the entries that should be moved to dst */
7591 for (j = 0; j < *nitems * nsize; j++) {
7592 n1 = old2new[(*items)[j]];
7594 counts[j / nsize]++; /* Count the atom belonging to dst */
7596 for (j = n2 = n3 = 0; j < *nitems; j++) {
7597 if (counts[j] > 0) {
7598 /* Remove from src */
7600 if (IntGroupAdd(del_g, j, 1) != 0)
7602 if (counts[j] == nsize) {
7605 if (IntGroupAdd(move_g, j, 1) != 0)
7611 /* Expand the destination array */
7612 if (items_dst != NULL && n3 > 0) {
7613 if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
7615 if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
7617 if (i == 0 && src->bondOrders != NULL) {
7618 if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), n3 - 1, NULL) == NULL)
7620 if (sCopyElementsFromArrayAtPositions(src->bondOrders, src->nbondOrders, dst->bondOrders, sizeof(Double), move_g) != 0)
7624 /* Remove from src */
7625 if (moveFlag && forUndo == 0) {
7626 if (nactions != NULL) {
7629 ip = (Int *)malloc(sizeof(Int) * nsize * n2);
7630 for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
7631 memmove(ip + j * nsize, *items + k * nsize, sizeof(Int) * nsize);
7632 if (i == 0 && src->bondOrders != NULL) {
7633 dp = (Double *)malloc(sizeof(Double) * n2);
7634 for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
7635 dp[j] = src->bondOrders[k];
7639 act = MolActionNew(gMolActionAddBondsForUndo, n2 * nsize, ip, del_g); break;
7641 act = MolActionNew(gMolActionAddAngles, n2 * nsize, ip, del_g); break;
7643 act = MolActionNew(gMolActionAddDihedrals, n2 * nsize, ip, del_g); break;
7645 act = MolActionNew(gMolActionAddImpropers, n2 * nsize, ip, del_g); break;
7648 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7653 act = MolActionNew(gMolActionAssignBondOrders, n2, dp, del_g);
7654 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7659 if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
7664 /* Renumber the entries */
7666 for (j = 0; j < *nitems * nsize; j++) {
7667 (*items)[j] = old2new[(*items)[j]];
7670 if (items_dst != NULL) {
7671 for (j = 0; j < *nitems_dst * nsize; j++) {
7672 (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
7676 IntGroupClear(move_g);
7677 IntGroupRelease(del_g);
7679 IntGroupRelease(move_g);
7681 /* Copy the residues */
7683 /* src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset) */
7684 n1 = src->nresidues - resSeqOffset; /* This will be dst->nresidues (if >0) */
7685 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
7688 memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
7692 /* Copy the parameters to dst */
7693 if (dst != NULL && dst_par_g != NULL && (n2 = IntGroupGetCount(dst_par_g)) > 0) {
7694 IntGroup *dst_new_g = IntGroupNew();
7695 Int dst_par_count[kLastParType - kFirstParType + 1];
7696 if (dst_new_g == NULL)
7698 for (i = 0; i <= kLastParType - kFirstParType; i++)
7699 dst_par_count[i] = 0;
7700 up = (UnionPar *)calloc(sizeof(UnionPar), n2);
7703 if (ParameterCopy(src->par, kFirstParType, up, dst_par_g) < n2)
7705 /* Renumber the explicit atom indices */
7706 for (i = 0; i < nsrc; i++)
7707 old2new[i] -= nsrcnew; /* new indices for atoms in dst; otherwise negative numbers */
7708 for (i = 0; i < n2; i++) {
7709 /* Renumber the indices, and count the number of parameters for each type */
7710 n1 = kFirstParType + IntGroupGetNthPoint(dst_par_g, i) / kParameterIndexOffset;
7711 dst_par_count[n1 - kFirstParType]++;
7712 ParameterRenumberAtoms(n1, up + i, nsrc, old2new);
7714 for (i = 0; i < nsrc; i++)
7715 old2new[i] += nsrcnew;
7716 if (dst->par == NULL)
7717 dst->par = ParameterNew();
7718 for (i = 0; i <= kLastParType - kFirstParType; i++) {
7719 if (dst_par_count[i] > 0)
7720 IntGroupAdd(dst_new_g, i * kParameterIndexOffset, dst_par_count[i]);
7722 if (ParameterInsert(dst->par, kFirstParType, up, dst_new_g) < n2)
7725 IntGroupRelease(dst_new_g);
7727 IntGroupRelease(dst_par_g);
7729 /* Remove the unused parameter. Note: the parameters that are in remove_par_g and not in
7730 dst_par_g will disappear. To support undo, these parameters should be taken care separately. */
7731 if (forUndo == 0 && remove_par_g != NULL && (n2 = IntGroupGetCount(remove_par_g)) > 0) {
7732 UnionPar *up = (UnionPar *)malloc(sizeof(UnionPar) * n2);
7733 ParameterDelete(src->par, kFirstParType, up, remove_par_g);
7734 if (nactions != NULL) {
7735 act = MolActionNew(gMolActionAddParameters, kFirstParType, remove_par_g, n2, up);
7736 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7741 IntGroupRelease(remove_par_g);
7743 /* Renumber the parameter records remaining in the src */
7745 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7746 n2 = ParameterGetCountForType(src->par, n1);
7747 for (i = 0; i < n2; i++) {
7748 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7749 ParameterRenumberAtoms(n1, up, nsrc, old2new);
7755 IntGroupRelease(remain_g);
7756 MoleculeCleanUpResidueTable(src);
7758 MoleculeCleanUpResidueTable(dst);
7761 src->nframes = -1; /* Should be recalculated later */
7763 dst->nframes = -1; /* Should be recalculated later */
7769 MoleculeIncrementModifyCount(src);
7770 src->needsMDRebuild = 1;
7771 __MoleculeUnlock(src);
7776 __MoleculeUnlock(src);
7777 /* Panic("Low memory while removing atoms"); */
7781 /* Separate molecule into two parts. The atoms specified by 'where' are moved
7782 from src to a new molecule, which is returned as *dstp. Dstp can be NULL,
7783 in which case the moved atoms are discarded. */
7785 MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
7787 return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1, nactions, actions, forUndo);
7790 /* Extract atoms from a given molecule into two parts. The atoms specified by
7791 'where' are copied from src to a new molecule, which is returned as *dstp.
7792 If dummyFlag is non-zero, then the atoms that are not included in the group
7793 but are connected to any atoms in the group are converted to "dummy" atoms
7794 (i.e. with element "Du" and names beginning with an underscore) and included
7795 in the new molecule object. */
7797 MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
7801 /* Extract the fragment */
7802 retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0, NULL, NULL, 0);
7808 /* Search bonds crossing the molecule border */
7809 IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
7811 IntGroupIterator iter;
7814 IntGroupIteratorInit(ig, &iter);
7815 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7816 /* The atoms at the border */
7819 n1 = src->bonds[i*2];
7820 n2 = src->bonds[i*2+1];
7821 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
7825 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
7826 continue; /* Actually this is an internal error */
7828 /* n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule */
7829 /* Create a new dummy atom with the same segment/residue info with n1
7830 and the same position as n2 */
7831 ap = ATOM_AT_INDEX(src->atoms, n1);
7832 memset(&a, 0, gSizeOfAtomRecord);
7833 a.segSeq = ap->segSeq;
7834 memmove(a.segName, ap->segName, 4);
7835 a.resSeq = ap->resSeq;
7836 memmove(a.resName, ap->resName, 4);
7837 ElementToString(0, a.element); /* "Du" */
7838 snprintf(a.aname, 4, "_%d", idx++);
7839 a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
7840 /* Add the dummy atom to the new molecule; nn[1] is the index
7841 of the new dummy atom in the new molecule */
7842 nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
7843 /* Connect nn1 and nn2 */
7844 nn[2] = kInvalidIndex;
7845 MoleculeAddBonds(*dstp, 1, nn, NULL, 1);
7847 IntGroupIteratorRelease(&iter);
7848 IntGroupRelease(ig);
7856 MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds, IntGroup *where, Int autoGenerate)
7858 Int nangles, ndihedrals;
7859 Int *angles, *dihedrals;
7860 Int i, j, k, kk, n1, n2, cn1, cn2;
7863 Atom *ap1, *ap2, *ap3;
7865 if (mp == NULL || bonds == NULL || nbonds <= 0)
7867 if (mp->noModifyTopology)
7868 return -4; /* Prohibited operation */
7870 /* Note: Duplicates and validity are not checked (the caller must do that) */
7875 if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, n1 + nbonds - 1, NULL) == NULL
7876 || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nbonds, sizeof(Int) * 2, where) != 0) {
7877 __MoleculeUnlock(mp);
7878 return -4; /* Out of memory */
7880 if (mp->bondOrders != NULL) {
7881 /* Expand the bond order info (all new entries are zero) */
7882 Double *dp = (Double *)calloc(sizeof(Double), nbonds);
7885 if (AssignArray(&(mp->bondOrders), &(mp->nbondOrders), sizeof(Double), n1 + nbonds - 1, NULL) == NULL
7886 || sInsertElementsToArrayAtPositions(mp->bondOrders, n1, dp, nbonds, sizeof(Double), where) != 0) {
7887 __MoleculeUnlock(mp);
7894 angles = dihedrals = NULL;
7895 nangles = ndihedrals = 0;
7897 /* Add connects[], and angles/dihedrals (if autoGenerate is true) */
7898 for (i = 0; i < nbonds; i++) {
7900 /* One entry at time */
7901 /* (Otherwise, duplicate entries of angles and dihedrals result) */
7903 n2 = bonds[i * 2 + 1];
7905 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
7906 AtomConnectInsertEntry(&ap1->connect, -1, n2);
7907 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
7908 AtomConnectInsertEntry(&ap2->connect, -1, n1);
7910 /* Add angles and dihedrals */
7912 AtomConnect *ac1, *ac2;
7913 if (ap1->anchor == NULL || ap2->anchor == NULL) {
7914 /* N1-N2-{XY} or N2-N1-{XY} angles (X: connected atom, Y: constitute atom of pi-anchor) */
7915 for (j = 0; j < 4; j++) {
7917 case 0: temp[0] = n1; temp[1] = n2; ac1 = &ap2->connect; break; /* N1-N2-X */
7918 case 1: if (ap2->anchor == NULL) continue; else ac1 = &ap2->anchor->connect; break; /* N1-N2-Y */
7919 case 2: temp[0] = n2; temp[1] = n1; ac1 = &ap1->connect; break; /* N2-N1-X */
7920 case 3: if (ap1->anchor == NULL) continue; else ac1 = &ap1->anchor->connect; break; /* N2-N1-Y */
7922 cp1 = AtomConnectData(ac1);
7924 for (k = 0; k < cn1; k++) {
7926 if (temp[2] == temp[0])
7928 ap3 = ATOM_AT_INDEX(mp->atoms, temp[2]);
7929 if (ap3->anchor != NULL) {
7930 /* Avoid X-anchor-anchor angle (anchor-X-anchor is allowed) */
7931 if ((j < 2 && ap2->anchor != NULL) || (j >= 2 && ap1->anchor != NULL))
7934 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7936 /* Dihedrals N1-N2-X-{XY} or N2-N1-X-{XY} */
7937 if (j == 1 || j == 3)
7939 cp2 = AtomConnectData(&ap3->connect);
7940 for (kk = 0; kk < ap3->connect.count; kk++) {
7942 if (temp[3] == temp[0] || temp[3] == temp[1])
7944 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7947 if (ap3->anchor != NULL) {
7948 /* N1-N2-X-Y or N2-N1-X-Y */
7949 /* for Y, only the first constitute atom is considered */
7950 cp2 = AtomConnectData(&ap3->anchor->connect);
7952 if (temp[3] == temp[0] || temp[3] == temp[1])
7954 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7960 /* X-N1-N2-X dihedrals */
7961 /* Y-N1-N2-anchor is allowed, but the force may be zero if the angle N1-N2-anchor is */
7962 /* close to 180 deg (e.g. in ferrocene, C-anchor-Fe-anchor dihedral should be k=0) */
7963 if (ap1->anchor == NULL) {
7964 ac1 = &ap1->connect;
7967 ac1 = &ap1->anchor->connect;
7968 cn1 = 1; /* Only the first constitute atom of pi-anchor is considered */
7970 if (ap2->anchor == NULL) {
7971 ac2 = &ap2->connect;
7974 ac2 = &ap2->anchor->connect;
7975 cn2 = 1; /* Only the first constitute atom of pi-anchor is considered */
7979 cp1 = AtomConnectData(ac1);
7980 cp2 = AtomConnectData(ac2);
7981 for (j = 0; j < cn1; j++) {
7983 if (temp[0] == temp[2])
7985 for (k = 0; k < cn2; k++) {
7987 if (temp[3] == temp[0] || temp[3] == temp[1])
7989 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7996 if (angles != NULL) {
7997 temp[0] = kInvalidIndex;
7998 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
8000 MoleculeAddAngles(mp, angles, NULL);
8003 if (dihedrals != NULL) {
8004 temp[0] = kInvalidIndex;
8005 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8007 MoleculeAddDihedrals(mp, dihedrals, NULL);
8011 MoleculeIncrementModifyCount(mp);
8012 mp->needsMDRebuild = 1;
8013 __MoleculeUnlock(mp);
8018 __MoleculeUnlock(mp);
8019 Panic("Low memory while adding bonds");
8020 return -1; /* Not reached */
8024 /* The deleted angles and dihedrals are stored in outRemoval. */
8025 /* (*outRemoval) is an array of integers, containing:
8026 [0..na*3-1]: the angle indices
8027 [na*3..na*3+nd*4-1]: the dihedral indices
8028 [na*3+nd*4..na*3+nd*4+ni*4-1]: the improper indices
8029 *outRemovedPos is an intgroup denoting the positions of the removed angles/dihedrals/impropers.
8030 the angle indices are included as they are,
8031 the dihedral indices are offset by ATOMS_MAX_NUMBER,
8032 the improper indices are offset by ATOMS_MAX_NUMBER*2.
8033 Note: the removed bond indices are not returned, because the caller should already know them. */
8035 MoleculeDeleteBonds(Molecule *mp, Int *bonds, IntGroup *where, Int **outRemoved, IntGroup **outRemovedPos)
8037 Int i, j, n1, n2, nw;
8038 Int *ip, *jp, na, nd, ni;
8039 IntGroup *ag, *dg, *ig;
8041 IntGroupIterator iter;
8045 if (mp->noModifyTopology)
8046 return -4; /* Prohibited operation */
8050 /* Update connects[] */
8051 IntGroupIteratorInit(where, &iter);
8052 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8053 n1 = mp->bonds[i * 2];
8054 n2 = mp->bonds[i * 2 + 1];
8055 ap = ATOM_AT_INDEX(mp->atoms, n1);
8056 ip = AtomConnectData(&ap->connect);
8057 for (j = 0; j < ap->connect.count; j++) {
8059 AtomConnectDeleteEntry(&ap->connect, j);
8063 ap = ATOM_AT_INDEX(mp->atoms, n2);
8064 ip = AtomConnectData(&ap->connect);
8065 for (j = 0; j < ap->connect.count; j++) {
8067 AtomConnectDeleteEntry(&ap->connect, j);
8073 /* Remove bonds, angles, dihedrals, impropers */
8078 nw = IntGroupGetCount(where);
8079 jp = (Int *)malloc(sizeof(Int) * nw * 2);
8081 IntGroupIteratorReset(&iter);
8082 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8083 jp[j++] = mp->bonds[i * 2];
8084 jp[j++] = mp->bonds[i * 2 + 1];
8086 IntGroupIteratorRelease(&iter);
8088 for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8089 for (j = 0; j < nw; j++) {
8092 if ((ip[0] == n1 && ip[1] == n2)
8093 || (ip[1] == n1 && ip[0] == n2)
8094 || (ip[1] == n1 && ip[2] == n2)
8095 || (ip[2] == n1 && ip[1] == n2)) {
8096 if (IntGroupAdd(ag, i, 1) != 0)
8103 for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8104 for (j = 0; j < nw; j++) {
8107 if ((ip[0] == n1 && ip[1] == n2)
8108 || (ip[1] == n1 && ip[0] == n2)
8109 || (ip[1] == n1 && ip[2] == n2)
8110 || (ip[2] == n1 && ip[1] == n2)
8111 || (ip[2] == n1 && ip[3] == n2)
8112 || (ip[3] == n1 && ip[2] == n2)) {
8115 if (IntGroupAdd(dg, i, 1) != 0)
8122 for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8123 for (j = 0; j < nw; j++) {
8126 if ((ip[0] == n1 && ip[2] == n2)
8127 || (ip[1] == n1 && ip[2] == n2)
8128 || (ip[3] == n1 && ip[2] == n2)
8129 || (ip[0] == n2 && ip[2] == n1)
8130 || (ip[1] == n2 && ip[2] == n1)
8131 || (ip[3] == n2 && ip[2] == n1)) {
8134 if (IntGroupAdd(ig, i, 1) != 0)
8143 if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, bonds, sizeof(Int) * 2, where) != 0)
8145 mp->nbonds -= IntGroupGetCount(where);
8146 if (mp->nbonds == 0) {
8150 if (mp->bondOrders != NULL) {
8151 if (sRemoveElementsFromArrayAtPositions(mp->bondOrders, mp->nbondOrders, NULL, sizeof(Double), where) != 0)
8153 mp->nbondOrders -= IntGroupGetCount(where);
8154 if (mp->nbondOrders == 0) {
8155 free(mp->bondOrders);
8156 mp->bondOrders = NULL;
8159 if (na == 0 && nd == 0 && ni == 0)
8162 ip = (Int *)malloc(sizeof(Int) * (na * 3 + nd * 4 + ni * 4));
8164 MoleculeDeleteAngles(mp, ip, ag);
8166 MoleculeDeleteDihedrals(mp, ip + na * 3, dg);
8168 MoleculeDeleteImpropers(mp, ip + na * 3 + nd * 4, ig);
8170 IntGroupOffset(dg, ATOMS_MAX_NUMBER);
8171 IntGroupOffset(ig, ATOMS_MAX_NUMBER * 2);
8172 IntGroupAddIntGroup(ag, dg);
8173 IntGroupAddIntGroup(ag, ig);
8174 IntGroupRelease(dg);
8175 IntGroupRelease(ig);
8178 if (IntGroupGetCount(ag) == 0) {
8179 IntGroupRelease(ag);
8184 *outRemovedPos = ag;
8186 MoleculeIncrementModifyCount(mp);
8187 mp->needsMDRebuild = 1;
8188 __MoleculeUnlock(mp);
8190 return na * 3 + nd * 4 + ni * 4;
8193 __MoleculeUnlock(mp);
8194 Panic("Low memory while removing bonds");
8195 return -1; /* Not reached */
8199 MoleculeAssignBondOrders(Molecule *mp, const Double *orders, IntGroup *where)
8202 IntGroupIterator iter;
8203 if (mp == NULL || orders == NULL || mp->nbonds == 0)
8205 if (mp->noModifyTopology)
8206 return -4; /* Prohibited operation */
8207 if (mp->bondOrders == NULL) {
8208 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbonds - 1, NULL);
8209 memset(mp->bondOrders, 0, sizeof(Double) * mp->nbondOrders);
8211 IntGroupIteratorInit(where, &iter);
8213 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8214 if (i >= mp->nbondOrders)
8216 mp->bondOrders[i] = orders[j++];
8218 IntGroupIteratorRelease(&iter);
8223 MoleculeGetBondOrders(Molecule *mp, Double *outOrders, IntGroup *where)
8226 IntGroupIterator iter;
8227 if (mp == NULL || mp->nbonds == 0)
8229 if (mp->bondOrders == NULL) {
8230 /* Returns all zero */
8231 i = IntGroupGetCount(where);
8232 for (j = 0; j < i; j++)
8235 IntGroupIteratorInit(where, &iter);
8237 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8238 if (i < mp->nbondOrders)
8239 outOrders[j] = mp->bondOrders[i];
8240 else outOrders[j] = 0.0;
8248 MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
8251 if (mp == NULL || angles == NULL)
8253 if (mp->noModifyTopology)
8254 return -4; /* Prohibited operation */
8258 nc = IntGroupGetCount(where);
8260 for (n1 = 0; angles[n1 * 3] >= 0; n1++)
8266 if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
8267 || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
8268 __MoleculeUnlock(mp);
8269 Panic("Low memory while adding angles");
8272 mp->needsMDRebuild = 1;
8273 __MoleculeUnlock(mp);
8278 MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
8281 if (mp == NULL || where == NULL)
8283 if (mp->noModifyTopology)
8284 return -4; /* Prohibited operation */
8286 if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
8287 __MoleculeUnlock(mp);
8288 Panic("Bad argument while deleting angles");
8290 mp->nangles -= (nc = IntGroupGetCount(where));
8291 if (mp->nangles == 0) {
8295 mp->needsMDRebuild = 1;
8296 __MoleculeUnlock(mp);
8301 MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
8304 if (mp == NULL || dihedrals == NULL)
8306 if (mp->noModifyTopology)
8307 return -4; /* Prohibited operation */
8309 nc = IntGroupGetCount(where);
8311 for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
8317 n1 = mp->ndihedrals;
8319 if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8320 || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
8321 __MoleculeUnlock(mp);
8322 Panic("Low memory while adding dihedrals");
8324 mp->needsMDRebuild = 1;
8325 __MoleculeUnlock(mp);
8330 MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
8333 if (mp == NULL || where == NULL)
8335 if (mp->noModifyTopology)
8336 return -4; /* Prohibited operation */
8338 if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
8339 __MoleculeUnlock(mp);
8340 Panic("Internal error: bad argument while deleting dihedrals");
8342 mp->ndihedrals -= (nc = IntGroupGetCount(where));
8343 if (mp->ndihedrals == 0) {
8344 free(mp->dihedrals);
8345 mp->dihedrals = NULL;
8347 mp->needsMDRebuild = 1;
8348 __MoleculeUnlock(mp);
8353 MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
8356 if (mp == NULL || impropers == NULL)
8358 if (mp->noModifyTopology)
8359 return -4; /* Prohibited operation */
8361 nc = IntGroupGetCount(where);
8363 for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
8369 n1 = mp->nimpropers;
8371 if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8372 || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
8373 __MoleculeUnlock(mp);
8374 Panic("Low memory while adding impropers");
8376 mp->needsMDRebuild = 1;
8377 __MoleculeUnlock(mp);
8382 MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
8385 if (mp == NULL || where == NULL)
8387 if (mp->noModifyTopology)
8388 return -4; /* Prohibited operation */
8390 if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
8391 __MoleculeUnlock(mp);
8392 Panic("Internal error: bad argument while deleting impropers");
8394 mp->nimpropers -= (nc = IntGroupGetCount(where));
8395 if (mp->impropers == NULL) {
8396 free(mp->impropers);
8397 mp->impropers = NULL;
8399 __MoleculeUnlock(mp);
8404 MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
8407 if (mp == NULL || mp->bonds == NULL)
8409 for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
8410 if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
8417 MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
8420 if (mp == NULL || mp->angles == NULL)
8422 for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8423 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
8424 (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
8431 MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8434 if (mp == NULL || mp->dihedrals == NULL)
8436 for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8437 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
8438 (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
8445 MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8448 if (mp == NULL || mp->impropers == NULL)
8450 for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8453 if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
8454 (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
8455 (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
8461 /* Remove the bond at bondIndex and create two dummy atoms instead.
8462 The dummy atoms are placed at the end of atoms[], and the residue
8463 numbers are the same as the root atoms (i.e. the atoms to which
8464 the dummy atoms are connected). The indices are returned in
8465 dummyIndices[0,1]. */
8467 MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
8469 Int roots[3], newBonds[5];
8475 if (mp == NULL || mp->noModifyTopology)
8477 if (bondIndex < 0 || bondIndex >= mp->nbonds)
8479 roots[0] = mp->bonds[bondIndex * 2];
8480 roots[1] = mp->bonds[bondIndex * 2 + 1];
8481 roots[2] = kInvalidIndex;
8482 rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
8483 rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
8484 VecSub(dr, rootp[0]->r, rootp[1]->r);
8485 for (i = 0; i < 2; i++) {
8488 memmove(nap, rootp[i], sizeof(na));
8489 nap->aname[0] = '*';
8490 strcpy(nap->element, "Du");
8492 nap->charge = nap->weight = 0.0;
8493 nap->atomicNumber = 0;
8494 nap->connect.count = 0;
8495 w = (i == 0 ? 0.4 : -0.4);
8496 VecScaleInc(nap->r, dr, w);
8503 /* Expand atoms array and append the dummy atoms at the end */
8505 natoms = mp->natoms;
8506 if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
8508 memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
8509 dummyIndices[0] = natoms;
8510 dummyIndices[1] = natoms + 1;
8512 /* Remove the old bond and create new bonds */
8513 ig = IntGroupNewWithPoints(bondIndex, 1, -1);
8516 MoleculeDeleteBonds(mp, NULL, ig, NULL, NULL);
8517 IntGroupRelease(ig);
8518 newBonds[0] = roots[0];
8519 newBonds[1] = dummyIndices[0];
8520 newBonds[2] = roots[1];
8521 newBonds[3] = dummyIndices[1];
8522 newBonds[4] = kInvalidIndex;
8524 i = (MoleculeAddBonds(mp, 2, newBonds, NULL, 1) < 0 ? -1 : 0);
8525 mp->needsMDRebuild = 1;
8526 __MoleculeUnlock(mp);
8530 __MoleculeUnlock(mp);
8531 Panic("Low memory during creating dummy atoms");
8535 /* Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
8536 a bond between the two root atoms. The value bondIndex is used as a
8537 hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
8538 the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
8539 is ignored and the new bond is stored at the end of bonds[]. */
8541 MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
8548 MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
8551 if (mol == NULL || mol->noModifyTopology)
8558 __MoleculeLock(mol);
8559 NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
8560 memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
8561 mol->needsMDRebuild = 1;
8562 __MoleculeUnlock(mol);
8569 MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
8572 if (mol == NULL || mol->noModifyTopology)
8574 n1 = mol->ndihedrals;
8575 np1 = mol->dihedrals;
8576 mol->ndihedrals = 0;
8577 mol->dihedrals = NULL;
8578 if (ndihedrals > 0) {
8579 __MoleculeLock(mol);
8580 NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
8581 memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
8582 mol->needsMDRebuild = 1;
8583 __MoleculeUnlock(mol);
8585 *outDihedrals = np1;
8590 MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
8593 if (mol == NULL || mol->noModifyTopology)
8595 n1 = mol->nimpropers;
8596 np1 = mol->impropers;
8597 mol->nimpropers = 0;
8598 mol->impropers = NULL;
8599 if (nimpropers > 0) {
8600 __MoleculeLock(mol);
8601 NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
8602 memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
8603 mol->needsMDRebuild = 1;
8604 __MoleculeUnlock(mol);
8606 *outImpropers = np1;
8612 MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
8619 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8620 return 0; /* molecule is empty */
8621 if (mol->noModifyTopology)
8625 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
8626 Int *cp = AtomConnectData(&ap->connect);
8627 if (ap->anchor != NULL)
8629 for (j = 0; j < ap->connect.count; j++) {
8631 if (ATOM_AT_INDEX(mol->atoms, j0)->anchor != NULL)
8633 for (k = j + 1; k < ap->connect.count; k++) {
8635 if (ATOM_AT_INDEX(mol->atoms, k0)->anchor != NULL)
8637 if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
8638 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
8647 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
8651 if (outAngles != NULL)
8652 *outAngles = angles;
8657 MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
8659 Int n1, n2, n3, n4, *ip, *cp2, *cp3;
8664 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8665 return 0; /* molecule is empty */
8668 for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
8669 Int i1, i3, i4, *ip;
8670 if (ap2->anchor != NULL)
8672 cp2 = AtomConnectData(&ap2->connect);
8673 for (i3 = 0; i3 < ap2->connect.count; i3++) {
8677 ap3 = ATOM_AT_INDEX(mol->atoms, n3);
8678 if (ap3->anchor != NULL)
8680 cp3 = AtomConnectData(&ap3->connect);
8681 for (i1 = 0; i1 < ap2->connect.count; i1++) {
8685 if (ATOM_AT_INDEX(mol->atoms, n1)->anchor != NULL)
8687 for (i4 = 0; i4 < ap3->connect.count; i4++) {
8689 if (n2 == n4 || n1 == n4)
8691 if (ATOM_AT_INDEX(mol->atoms, n4)->anchor != NULL)
8693 if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
8694 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
8704 if (ndihedrals > 0) {
8705 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
8709 if (outDihedrals != NULL)
8710 *outDihedrals = dihedrals;
8715 MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
8717 Int n1, n2, n3, n4, t1, t2, t3, t4, *ip, *cp;
8718 Parameter *par = mol->par;
8723 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8724 return 0; /* molecule is empty */
8725 if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
8726 return 0; /* No improper parameters are defined */
8730 for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
8731 Int i1, i2, i4, found, *ip;
8733 cp = AtomConnectData(&ap3->connect);
8734 for (i1 = 0; i1 < ap3->connect.count; i1++) {
8736 t1 = ATOM_AT_INDEX(ap, n1)->type;
8737 for (i2 = i1 + 1; i2 < ap3->connect.count; i2++) {
8739 t2 = ATOM_AT_INDEX(ap, n2)->type;
8740 for (i4 = i2 + 1; i4 < ap3->connect.count; i4++) {
8742 t4 = ATOM_AT_INDEX(ap, n4)->type;
8744 if (ParameterLookupImproperPar(par, t1, t2, t3, t4, n1, n2, n3, n4, 0) != NULL)
8746 else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, -1, -1, -1, -1, 0) != NULL)
8748 if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
8749 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8759 if (nimpropers > 0) {
8760 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8764 if (outImpropers != NULL)
8765 *outImpropers = impropers;
8769 #pragma mark ====== Residues ======
8772 MoleculeCleanUpResidueTable(Molecule *mp)
8776 if (mp == NULL || mp->natoms == 0)
8780 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8781 if (ap->resSeq >= maxres)
8782 maxres = ap->resSeq + 1;
8783 if (ap->resSeq < mp->nresidues) {
8784 if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
8785 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8787 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
8790 if (maxres < mp->nresidues)
8791 mp->nresidues = maxres;
8792 __MoleculeUnlock(mp);
8795 /* Change the number of residues. If nresidues is greater than the current value,
8796 then the array mp->residues is expanded with null names. If nresidues is smaller
8797 than the current value, mp->nresidues is set to the smallest possible value
8798 that is no smaller than nresidues and larger than any of the resSeq values. */
8800 MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
8805 if (mp->nresidues == nresidues)
8807 else if (mp->nresidues < nresidues) {
8810 AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
8811 while (n < nresidues)
8812 mp->residues[n++][0] = 0;
8813 __MoleculeUnlock(mp);
8819 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8820 if (ap->resSeq >= n)
8829 MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
8831 IntGroupIterator iter;
8832 int withArray, resSeq, maxSeq;
8836 /* If LSB of resSeqs is 1, then a constant value is used for all specified atoms */
8837 if (((int)resSeqs & 1) == 0) {
8842 resSeq = ((int)resSeqs - 1) / 2;
8845 IntGroupIteratorInit(group, &iter);
8847 /* Change resSeqs */
8851 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8852 ap = ATOM_AT_INDEX(mp->atoms, i);
8854 resSeq = resSeqs[j++];
8855 if (resSeq > maxSeq)
8857 ap->resSeq = resSeq;
8859 __MoleculeUnlock(mp);
8861 /* Expand array if necessary */
8862 if (maxSeq >= mp->nresidues)
8863 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8865 /* Synchronize resName and residues[] */
8867 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8868 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8870 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8872 IntGroupIteratorRelease(&iter);
8873 __MoleculeUnlock(mp);
8875 MoleculeIncrementModifyCount(mp);
8881 MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
8883 return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(resSeq * 2 + 1));
8886 /* Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
8887 specifies the mp->nresidues after modifying the residue numbers.
8888 If all atoms are modified, then the table of residue names is also shifted. Otherwise,
8889 the table of residue names is not touched. */
8891 MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
8893 int i, maxSeq, nmodatoms;
8895 IntGroupIterator iter;
8896 IntGroupIteratorInit(group, &iter);
8899 nresidues = mp->nresidues;
8902 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8903 ap = ATOM_AT_INDEX(mp->atoms, i);
8904 ap->resSeq += offset;
8905 if (ap->resSeq < 0) {
8906 /* Bad argument; undo change and returns this index + 1 */
8908 ap->resSeq -= offset;
8909 while ((i = IntGroupIteratorLast(&iter)) >= 0) {
8910 ap = ATOM_AT_INDEX(mp->atoms, i);
8911 ap->resSeq -= offset;
8913 IntGroupIteratorRelease(&iter);
8914 return bad_index + 1;
8916 if (ap->resSeq > maxSeq)
8917 maxSeq = ap->resSeq;
8920 if (maxSeq >= nresidues)
8921 nresidues = maxSeq + 1;
8922 if (offset < 0 && nmodatoms == mp->natoms) {
8923 /* Shift the residue names downward */
8924 memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
8926 __MoleculeUnlock(mp);
8927 MoleculeChangeNumberOfResidues(mp, nresidues);
8928 if (offset > 0 && nmodatoms == mp->natoms) {
8929 /* Shift the residue names upward */
8931 memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
8932 __MoleculeUnlock(mp);
8934 IntGroupIteratorRelease(&iter);
8936 MoleculeIncrementModifyCount(mp);
8941 /* Change residue names for the specified residue numbers. Names is an array of
8942 chars containing argc*4 characters, and every 4 characters represent a
8943 residue name; characters '\x01'-'\x1f' are converted to '\0', which allow
8944 names to be handled as a C string. */
8946 MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
8951 for (i = 0; i < argc; i++) {
8952 if (maxSeq < resSeqs[i])
8953 maxSeq = resSeqs[i];
8955 if (maxSeq >= mp->nresidues)
8956 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8958 for (i = 0; i < argc; i++) {
8959 char *p = mp->residues[resSeqs[i]];
8961 strncpy(p, names + i * 4, 4);
8962 for (j = 0; j < 4; j++) {
8963 if (p[j] >= 0 && p[j] < 0x20)
8967 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8968 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8970 __MoleculeUnlock(mp);
8972 MoleculeIncrementModifyCount(mp);
8977 /* Returns the maximum residue number actually used */
8979 MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
8984 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8985 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8987 if (ap->resSeq > maxSeq)
8988 maxSeq = ap->resSeq;
8993 /* Returns the minimum residue number actually used */
8995 MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
8999 minSeq = ATOMS_MAX_NUMBER;
9000 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9001 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9003 if (ap->resSeq < minSeq)
9004 minSeq = ap->resSeq;
9006 return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
9009 #pragma mark ====== Sort by Residues ======
9012 sAtomSortComparator(const void *a, const void *b)
9014 const Atom *ap, *bp;
9015 ap = *((const Atom **)a);
9016 bp = *((const Atom **)b);
9017 if (ap->resSeq == bp->resSeq) {
9018 /* Retain the original order (i.e. atom with larger pointer address is larger) */
9025 /* Compare the residue sequence. However, residue sequence 0 is always larger. */
9026 if (ap->resSeq == 0)
9028 else if (bp->resSeq == 0)
9030 else if (ap->resSeq < bp->resSeq)
9032 else if (ap->resSeq > bp->resSeq)
9039 sMoleculeReorder(Molecule *mp)
9041 int i, res, prevRes;
9045 if (mp == NULL || mp->natoms <= 1)
9048 /* Sort the atoms, bonds, etc. */
9049 apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
9050 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9051 newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9052 if (apArray == NULL || old2new == NULL || newAtoms == NULL)
9053 Panic("Low memory during reordering atoms");
9054 for (i = 0; i < mp->natoms; i++)
9055 apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
9057 /* Sort the atoms. Note: apArray is an array of "Pointer to Atom" */
9058 qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
9060 /* Make a table of 'which atom becomes which' */
9061 for (i = 0; i < mp->natoms; i++) {
9062 int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
9066 /* Renumber the bonds, etc. */
9067 for (i = 0; i < mp->nbonds * 2; i++) {
9068 mp->bonds[i] = old2new[mp->bonds[i]];
9070 for (i = 0; i < mp->nangles * 3; i++) {
9071 mp->angles[i] = old2new[mp->angles[i]];
9073 for (i = 0; i < mp->ndihedrals * 4; i++) {
9074 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9076 for (i = 0; i < mp->nimpropers * 4; i++) {
9077 mp->impropers[i] = old2new[mp->impropers[i]];
9079 for (i = 0; i < mp->natoms; i++) {
9081 ip = AtomConnectData(&(apArray[i]->connect));
9082 for (j = 0; j < apArray[i]->connect.count; j++, ip++)
9086 /* Renumber the residues so that the residue numbers are contiguous */
9088 for (i = 0; i < mp->natoms; i++) {
9089 if (apArray[i]->resSeq == 0)
9091 if (apArray[i]->resSeq != prevRes) {
9093 prevRes = apArray[i]->resSeq;
9094 if (prevRes != res) {
9095 strncpy(mp->residues[res], mp->residues[prevRes], 4);
9098 apArray[i]->resSeq = res;
9100 mp->nresidues = res + 1;
9102 /* Sort the atoms and copy back to atoms[] */
9103 for (i = 0; i < mp->natoms; i++) {
9104 memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
9106 memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
9108 /* Free the locally allocated storage */
9114 /* Renumber atoms */
9116 MoleculeRenumberAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
9118 Int *old2new, i, j, retval;
9122 if (mp->noModifyTopology)
9124 if (old2new_out != NULL)
9125 old2new = old2new_out;
9127 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9128 saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9129 if (old2new == NULL || saveAtoms == NULL)
9130 Panic("Low memory during reordering atoms");
9131 memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
9133 for (i = 0; i < mp->natoms; i++)
9135 for (i = 0; i < isize && i < mp->natoms; i++) {
9137 if (j < 0 || j >= mp->natoms) {
9138 retval = 1; /* Out of range */
9141 if (old2new[j] != -1) {
9142 retval = 2; /* Duplicate entry */
9147 if (i < mp->natoms) {
9148 for (j = 0; j < mp->natoms; j++) {
9149 if (old2new[j] != -1)
9154 if (i != mp->natoms) {
9155 retval = 3; /* Internal inconsistency */
9159 /* Renumber the bonds, etc. */
9160 for (i = 0; i < mp->nbonds * 2; i++) {
9161 mp->bonds[i] = old2new[mp->bonds[i]];
9163 for (i = 0; i < mp->nangles * 3; i++) {
9164 mp->angles[i] = old2new[mp->angles[i]];
9166 for (i = 0; i < mp->ndihedrals * 4; i++) {
9167 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9169 for (i = 0; i < mp->nimpropers * 4; i++) {
9170 mp->impropers[i] = old2new[mp->impropers[i]];
9172 /* Renumber the connection table and pi anchor table */
9173 for (i = 0; i < mp->natoms; i++) {
9174 Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
9175 Int *ip = AtomConnectData(&ap->connect);
9176 for (j = 0; j < ap->connect.count; j++, ip++)
9178 if (ap->anchor != NULL) {
9179 ip = AtomConnectData(&ap->anchor->connect);
9180 for (j = 0; j < ap->anchor->connect.count; j++, ip++)
9185 if (mp->par != NULL) {
9186 /* Renumber the parameters */
9188 for (j = kFirstParType; j <= kLastParType; j++) {
9189 n = ParameterGetCountForType(mp->par, j);
9190 for (i = 0; i < n; i++) {
9191 UnionPar *up = ParameterGetUnionParFromTypeAndIndex(mp->par, j, i);
9193 ParameterRenumberAtoms(j, up, mp->natoms, old2new);
9198 /* Renumber the atoms */
9199 for (i = 0; i < mp->natoms; i++)
9200 memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
9203 MoleculeIncrementModifyCount(mp);
9204 mp->needsMDRebuild = 1;
9207 __MoleculeUnlock(mp);
9209 if (old2new_out == NULL)
9214 #pragma mark ====== Coordinate Transform ======
9217 MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
9222 Transform rtr, symtr;
9223 if (mp == NULL || tr == NULL)
9225 TransformInvert(rtr, tr);
9227 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9228 if (group == NULL || IntGroupLookup(group, i, NULL) != 0) {
9229 TransformVec(&ap->r, tr, &ap->r);
9230 if (!SYMOP_ALIVE(ap->symop))
9232 /* Transform symop */
9233 if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9235 TransformMul(symtr, tr, symtr);
9236 if (group == NULL || IntGroupLookup(group, ap->symbase, NULL) != 0)
9237 TransformMul(symtr, symtr, rtr);
9239 if (!SYMOP_ALIVE(ap->symop))
9241 /* Transform symop if the base atom is transformed */
9242 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
9244 if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9246 TransformMul(symtr, symtr, rtr);
9248 if (MoleculeGetSymopForTransform(mp, symtr, &new_symop, 1) != 0)
9250 ap->symop = new_symop;
9252 mp->needsMDCopyCoordinates = 1;
9253 __MoleculeUnlock(mp);
9254 sMoleculeNotifyChangeAppearance(mp);
9259 MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
9263 if (mp == NULL || tr == NULL)
9266 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9267 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9269 TransformVec(&ap->r, tr, &ap->r);
9271 mp->needsMDCopyCoordinates = 1;
9272 __MoleculeUnlock(mp);
9273 sMoleculeNotifyChangeAppearance(mp);
9278 MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
9281 if (mp == NULL || vp == NULL)
9283 memset(tr, 0, sizeof(tr));
9284 tr[0] = tr[4] = tr[8] = 1.0;
9288 MoleculeTransform(mp, tr, group);
9292 MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
9295 TransformForRotation(tr, axis, angle, center);
9296 MoleculeTransform(mp, tr, group);
9300 MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
9305 if (mp == NULL || center == NULL)
9307 if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9308 return 2; /* Empty molecule */
9310 center->x = center->y = center->z = 0.0;
9311 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9312 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9314 VecScaleInc(*center, ap->r, ap->weight);
9318 return 3; /* Atomic weights are not defined? */
9320 VecScaleSelf(*center, w);
9325 MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
9332 if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9333 return 2; /* Empty molecule */
9334 vmin.x = vmin.y = vmin.z = 1e50;
9335 vmax.x = vmax.y = vmax.z = -1e50;
9336 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9337 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9339 if (vmin.x > ap->r.x)
9341 if (vmin.y > ap->r.y)
9343 if (vmin.z > ap->r.z)
9345 if (vmax.x < ap->r.x)
9347 if (vmax.y < ap->r.y)
9349 if (vmax.z < ap->r.z)
9359 #pragma mark ====== Measurements ======
9362 MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
9365 /* if (mp->is_xtal_coord) {
9366 TransformVec(&r1, mp->cell->tr, vp1);
9367 TransformVec(&r2, mp->cell->tr, vp2);
9373 return VecLength(r1);
9377 MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
9381 /* if (mp->is_xtal_coord) {
9382 TransformVec(&r1, mp->cell->tr, vp1);
9383 TransformVec(&r2, mp->cell->tr, vp2);
9384 TransformVec(&r3, mp->cell->tr, vp3);
9392 w = VecLength(r1) * VecLength(r3);
9395 return acos(VecDot(r1, r3) / w) * kRad2Deg;
9399 MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
9401 Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
9403 /* if (mp->is_xtal_coord) {
9404 TransformVec(&r1, mp->cell->tr, vp1);
9405 TransformVec(&r2, mp->cell->tr, vp2);
9406 TransformVec(&r3, mp->cell->tr, vp3);
9407 TransformVec(&r4, mp->cell->tr, vp4);
9414 VecSub(r21, r1, r2);
9415 VecSub(r32, r2, r3);
9416 VecSub(r43, r3, r4);
9417 VecCross(v1, r21, r32);
9418 VecCross(v2, r32, r43);
9419 VecCross(v3, r32, v1);
9423 if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
9429 VecScaleSelf(v1, w1);
9430 VecScaleSelf(v2, w2);
9431 VecScaleSelf(v3, w3);
9432 return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * kRad2Deg;
9436 #pragma mark ====== XtalCell Parameters ======
9439 MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
9441 if (mp->cell != NULL) {
9442 TransformVec(dst, mp->cell->tr, src);
9447 MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
9449 if (mp->cell != NULL) {
9450 TransformVec(dst, mp->cell->rtr, src);
9455 MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
9457 static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
9459 Vector *vp1, *vp2, *vp3;
9464 for (n1 = 0; n1 < 3; n1++) {
9465 if (cp->flags[n1] != 0)
9469 /* All directions are non-periodic */
9470 memmove(&(cp->tr), &identityTransform, sizeof(Transform));
9471 memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
9475 vp1 = &(cp->axes[n1]);
9476 vp2 = &(cp->axes[n2]);
9477 vp3 = &(cp->axes[n3]);
9478 cp->tr[n1*3] = vp1->x;
9479 cp->tr[n1*3+1] = vp1->y;
9480 cp->tr[n1*3+2] = vp1->z;
9481 cp->tr[9] = cp->origin.x;
9482 cp->tr[10] = cp->origin.y;
9483 cp->tr[11] = cp->origin.z;
9484 if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
9485 /* 1-dimensional or 2-dimensional system */
9486 /* Create "dummy" axes, so that transforms between internal and cartesian coordinates are
9487 possible with a single matrix */
9488 if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
9490 static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
9491 VecCross(v1, *vp1, xvec);
9492 VecCross(v2, *vp1, yvec);
9493 if (VecLength2(v1) < VecLength2(v2))
9495 VecCross(v2, *vp1, v1);
9496 if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
9497 return -1; /* Non-regular transform */
9498 } else if (cp->flags[n2] == 0) {
9500 VecCross(v1, v2, *vp1);
9501 if (NormalizeVec(&v1, &v1))
9502 return -1; /* Non-regular transform */
9505 VecCross(v2, *vp1, v1);
9506 if (NormalizeVec(&v2, &v2))
9507 return -1; /* Non-regular transform */
9509 cp->tr[n2*3] = v1.x;
9510 cp->tr[n2*3+1] = v1.y;
9511 cp->tr[n2*3+2] = v1.z;
9512 cp->tr[n3*3] = v2.x;
9513 cp->tr[n3*3+1] = v2.y;
9514 cp->tr[n3*3+2] = v2.z;
9516 VecCross(v1, *vp1, *vp2);
9517 if (fabs(VecDot(v1, *vp3)) < 1e-7)
9518 return -1; /* Non-regular transform */
9519 cp->tr[n2*3] = vp2->x;
9520 cp->tr[n2*3+1] = vp2->y;
9521 cp->tr[n2*3+2] = vp2->z;
9522 cp->tr[n3*3] = vp3->x;
9523 cp->tr[n3*3+1] = vp3->y;
9524 cp->tr[n3*3+2] = vp3->z;
9527 if (TransformInvert(cp->rtr, cp->tr))
9528 return -1; /* Non-regular transform */
9530 /* Calculate the reciprocal cell parameters */
9531 cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[3] * cp->rtr[3] + cp->rtr[6] * cp->rtr[6]);
9532 cp->rcell[1] = sqrt(cp->rtr[1] * cp->rtr[1] + cp->rtr[4] * cp->rtr[4] + cp->rtr[7] * cp->rtr[7]);
9533 cp->rcell[2] = sqrt(cp->rtr[2] * cp->rtr[2] + cp->rtr[5] * cp->rtr[5] + cp->rtr[8] * cp->rtr[8]);
9534 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;
9535 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;
9536 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;
9539 /* Calculate a, b, c, alpha, beta, gamma */
9540 cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[1] * cp->tr[1] + cp->tr[2] * cp->tr[2]);
9541 cp->cell[1] = sqrt(cp->tr[3] * cp->tr[3] + cp->tr[4] * cp->tr[4] + cp->tr[5] * cp->tr[5]);
9542 cp->cell[2] = sqrt(cp->tr[6] * cp->tr[6] + cp->tr[7] * cp->tr[7] + cp->tr[8] * cp->tr[8]);
9543 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;
9544 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;
9545 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;
9551 MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
9560 memset(&cmat, 0, sizeof(Transform));
9561 if (mp->cell != NULL)
9562 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
9564 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
9566 if (mp->cell != NULL) {
9568 mp->needsMDRebuild = 1;
9574 cp = (XtalCell *)calloc(sizeof(XtalCell), 1);
9576 Panic("Low memory during setting cell parameters");
9578 mp->needsMDRebuild = 1;
9580 /* alpha, beta, gamma are in degree */
9584 cp->cell[3] = alpha;
9586 cp->cell[5] = gamma;
9587 if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
9588 /* c unique (hexagonal etc.) */
9589 Double cosa, cosb, sinb, cosg;
9590 cosa = cos(alpha * kDeg2Rad);
9591 cosb = cos(beta * kDeg2Rad);
9592 sinb = sin(beta * kDeg2Rad);
9593 cosg = cos(gamma * kDeg2Rad);
9594 cp->axes[0].x = a * sinb;
9596 cp->axes[0].z = a * cosb;
9597 cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
9598 cp->axes[1].z = b * cosa;
9599 cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
9605 Double cosg, sing, cosa, cosb;
9606 cosa = cos(alpha * kDeg2Rad);
9607 cosb = cos(beta * kDeg2Rad);
9608 cosg = cos(gamma * kDeg2Rad);
9609 sing = sin(gamma * kDeg2Rad);
9610 cp->axes[0].x = a * sing;
9611 cp->axes[0].y = a * cosg;
9616 cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
9617 cp->axes[2].y = c * cosa;
9618 cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
9620 cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
9621 cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
9622 MoleculeCalculateCellFromAxes(cp, 0);
9623 TransformMul(cmat, cp->tr, cmat);
9626 /* Update the coordinates (if requested) */
9627 if (convertCoordinates) {
9628 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9629 TransformVec(&(ap->r), cmat, &(ap->r));
9633 /* Update the anisotropic parameters */
9634 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9635 Aniso *anp = ap->aniso;
9637 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
9640 __MoleculeUnlock(mp);
9641 sMoleculeNotifyChangeAppearance(mp);
9645 MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x13, Double x23, const Double *sigmaptr)
9649 const Double log2 = 0.693147180559945;
9650 const Double pi22 = 19.7392088021787; /* 2*pi**2 */
9656 if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
9658 anp = mp->atoms[n1].aniso;
9661 anp = (Aniso *)calloc(sizeof(Aniso), 1);
9663 __MoleculeUnlock(mp);
9664 Panic("Low memory during setting anisotropic atom parameters");
9666 mp->atoms[n1].aniso = anp;
9669 case 1: d = 1; dx = 0.5; break;
9670 case 2: d = log2; dx = log2; break;
9671 case 3: d = log2; dx = log2 * 0.5; break;
9672 case 4: u = 1; d = 0.25; dx = 0.25; break;
9673 case 5: u = 1; d = 0.25; dx = 0.125; break;
9674 case 8: u = 1; d = pi22; dx = pi22; break;
9675 case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
9676 case 10: d = pi22; dx = pi22; break;
9677 default: d = dx = 1; break;
9679 anp->bij[0] = x11 * d;
9680 anp->bij[1] = x22 * d;
9681 anp->bij[2] = x33 * d;
9682 anp->bij[3] = x12 * dx;
9683 anp->bij[4] = x13 * dx;
9684 anp->bij[5] = x23 * dx;
9685 if (sigmaptr != NULL) {
9687 anp->bsig[0] = sigmaptr[0] * d;
9688 anp->bsig[1] = sigmaptr[1] * d;
9689 anp->bsig[2] = sigmaptr[2] * d;
9690 anp->bsig[3] = sigmaptr[3] * dx;
9691 anp->bsig[4] = sigmaptr[4] * dx;
9692 anp->bsig[5] = sigmaptr[5] * dx;
9695 anp->bsig[0] = anp->bsig[1] = anp->bsig[2] = anp->bsig[3] = anp->bsig[4] = anp->bsig[5] = 0.0;
9698 if (cp != NULL && u == 1) {
9699 anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
9700 anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
9701 anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
9702 anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
9703 anp->bij[4] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[3] * kDeg2Rad); */
9704 anp->bij[5] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[4] * kDeg2Rad); */
9705 if (sigmaptr != NULL) {
9706 anp->bsig[0] *= cp->rcell[0] * cp->rcell[0];
9707 anp->bsig[1] *= cp->rcell[1] * cp->rcell[1];
9708 anp->bsig[2] *= cp->rcell[2] * cp->rcell[2];
9709 anp->bsig[3] *= cp->rcell[0] * cp->rcell[1];
9710 anp->bsig[4] *= cp->rcell[2] * cp->rcell[0];
9711 anp->bsig[5] *= cp->rcell[1] * cp->rcell[2];
9715 /* Calculate the principal axes (in Cartesian coordinates) */
9716 /* The principal axes are the eigenvectors of matrix At(B^-1)A, where
9717 B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
9718 in which x and z are the crystal-space and cartesian coordinates. */
9719 m1[0] = anp->bij[0] / pi22;
9720 m1[4] = anp->bij[1] / pi22;
9721 m1[8] = anp->bij[2] / pi22;
9722 m1[1] = m1[3] = anp->bij[3] / pi22;
9723 m1[2] = m1[6] = anp->bij[4] / pi22;
9724 m1[5] = m1[7] = anp->bij[5] / pi22;
9725 MatrixInvert(m1, m1);
9727 memmove(m2, cp->rtr, sizeof(Mat33));
9728 MatrixMul(m1, m1, m2);
9729 MatrixTranspose(m2, m2);
9730 MatrixMul(m1, m2, m1);
9732 MatrixSymDiagonalize(m1, val, axis);
9733 for (u = 0; u < 3; u++) {
9735 fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
9738 val[u] = 1 / sqrt(val[u]);
9740 anp->pmat[u*3] = axis[u].x * val[u];
9741 anp->pmat[u*3+1] = axis[u].y * val[u];
9742 anp->pmat[u*3+2] = axis[u].z * val[u];
9744 __MoleculeUnlock(mp);
9747 /* Set the anisotropic parameter for atom idx according to the symop. If symop is not alive, nothing is done. */
9749 MoleculeSetAnisoBySymop(Molecule *mp, int idx)
9753 if (mp == NULL || idx < 0 || idx >= mp->natoms)
9755 ap = ATOM_AT_INDEX(mp->atoms, idx);
9756 if (!SYMOP_ALIVE(ap->symop))
9758 ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
9759 if (ap2->aniso == NULL) {
9760 if (ap->aniso != NULL) {
9766 if (ap->aniso == NULL)
9767 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
9768 if (ap->symop.sym == 0 || ap->symop.sym >= mp->nsyms) {
9769 /* Just copy the aniso parameters */
9770 memmove(ap->aniso, ap2->aniso, sizeof(Aniso));
9773 memmove(t1, SYMMETRY_AT_INDEX(mp->syms, ap->symop.sym), sizeof(Transform));
9774 t1[9] = t1[10] = t1[11] = 0.0;
9775 memset(t2, 0, sizeof(Transform));
9776 t2[0] = ap2->aniso->bij[0];
9777 t2[4] = ap2->aniso->bij[1];
9778 t2[8] = ap2->aniso->bij[2];
9779 t2[1] = t2[3] = ap2->aniso->bij[3];
9780 t2[2] = t2[6] = ap2->aniso->bij[4];
9781 t2[5] = t2[7] = ap2->aniso->bij[5];
9782 TransformMul(t2, t1, t2);
9783 TransformInvert(t1, t1);
9784 TransformMul(t2, t2, t1);
9785 MoleculeSetAniso(mp, idx, 0, t2[0], t2[4], t2[8], t2[1], t2[2], t2[5], (ap2->aniso->has_bsig ? ap2->aniso->bsig : NULL));
9789 MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic, int convertCoordinates)
9791 static Vector zeroVec = {0, 0, 0};
9798 if (mp->cell != NULL)
9799 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
9801 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
9803 if (mp->cell != NULL) {
9805 mp->needsMDRebuild = 1;
9810 memset(&b, 0, sizeof(b));
9811 b.axes[0] = (ax != NULL ? *ax : zeroVec);
9812 b.axes[1] = (ay != NULL ? *ay : zeroVec);
9813 b.axes[2] = (az != NULL ? *az : zeroVec);
9815 memmove(b.flags, periodic, 3);
9816 if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
9819 if (mp->cell == NULL) {
9820 mp->needsMDRebuild = 1;
9822 if (mp->cell->has_sigma) {
9823 /* Keep the sigma */
9825 memmove(b.cellsigma, mp->cell->cellsigma, sizeof(mp->cell->cellsigma));
9827 if ((b.flags[0] != mp->cell->flags[0]) || (b.flags[1] != mp->cell->flags[1]) || (b.flags[2] != mp->cell->flags[2])) {
9828 mp->needsMDRebuild = 1;
9832 mp->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
9833 if (mp->cell != NULL) {
9834 memmove(mp->cell, &b, sizeof(XtalCell));
9835 TransformMul(cmat, b.tr, cmat);
9836 /* Update the coordinates (if requested) */
9837 if (convertCoordinates) {
9838 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9839 TransformVec(&(ap->r), cmat, &(ap->r));
9843 /* Update the anisotropic parameters */
9844 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9845 Aniso *anp = ap->aniso;
9847 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
9851 } else n = -2; /* Out of memory */
9852 __MoleculeUnlock(mp);
9853 sMoleculeNotifyChangeAppearance(mp);
9857 #pragma mark ====== Fragment manipulation ======
9860 sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
9864 if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
9866 IntGroupAdd(result, idx, 1);
9867 ap = ATOM_AT_INDEX(mp->atoms, idx);
9868 cp = AtomConnectData(&ap->connect);
9869 for (i = 0; i < ap->connect.count; i++) {
9871 if (IntGroupLookup(result, idx2, NULL))
9873 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, idx2)->anchor != NULL)
9874 continue; /* bond between two pi_anchors is ignored */
9875 sMoleculeFragmentSub(mp, idx2, result, exatoms);
9877 if (ap->anchor != NULL) {
9878 cp = AtomConnectData(&ap->anchor->connect);
9879 for (i = 0; i < ap->anchor->connect.count; i++) {
9881 if (IntGroupLookup(result, idx2, NULL))
9883 sMoleculeFragmentSub(mp, idx2, result, exatoms);
9888 /* The molecular fragment (= interconnected atoms) containing the atom n1 and
9889 not containing the atoms in exatoms */
9891 MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
9894 if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9896 result = IntGroupNew();
9897 sMoleculeFragmentSub(mp, n1, result, exatoms);
9901 /* The molecular fragment (= interconnected atoms) containing the atom n1 and
9902 not containing the atoms n2, n3, ... (terminated by -1) */
9904 MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
9907 IntGroup *exatoms, *result;
9908 if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9910 exatoms = IntGroupNew();
9911 for (i = 0; i < argc; i++)
9912 IntGroupAdd(exatoms, argv[i], 1);
9913 result = IntGroupNew();
9914 sMoleculeFragmentSub(mp, n1, result, exatoms);
9915 IntGroupRelease(exatoms);
9919 /* The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
9920 not containing the atoms in exatoms */
9922 MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
9924 IntGroupIterator iter;
9927 if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
9929 IntGroupIteratorInit(inatoms, &iter);
9930 result = IntGroupNew();
9931 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9932 sMoleculeFragmentSub(mp, i, result, exatoms);
9934 IntGroupIteratorRelease(&iter);
9938 /* Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
9939 group is bound to the rest of the molecule via only one bond.
9940 If the result is true, then the atoms belonging to the (only) bond are returned
9941 in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
9942 and n2 can be NULL, if those informations are not needed. */
9944 MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
9946 Int i, i1, i2, j, k, bond_count, nval1, nval2, *cp;
9948 if (mp == NULL || mp->natoms == 0 || group == NULL)
9949 return 0; /* Invalid arguments */
9951 for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
9952 i2 = IntGroupGetEndPoint(group, i);
9953 for (j = i1; j < i2; j++) {
9954 if (j < 0 || j >= mp->natoms)
9955 return 0; /* Invalid atom group */
9956 ap = ATOM_AT_INDEX(mp->atoms, j);
9957 cp = AtomConnectData(&ap->connect);
9958 for (k = 0; k < ap->connect.count; k++) {
9959 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, cp[k])->anchor != NULL)
9960 continue; /* Ignore bond between two pi_anchors */
9961 if (IntGroupLookup(group, cp[k], NULL) == 0) {
9966 return 0; /* Too many bonds */
9969 if (ap->anchor != NULL) {
9970 cp = AtomConnectData(&ap->anchor->connect);
9971 for (k = 0; k < ap->anchor->connect.count; k++) {
9972 if (IntGroupLookup(group, cp[k], NULL) == 0) {
9977 return 0; /* Too many bonds */
9983 if (bond_count == 1) {
9994 /* Returns non-zero if the given group is 'rotatable' in the molecule. The group
9995 is said to be 'rotatable' when either of the following conditions are met; (1)
9996 the group is detachable, or (2) the group consists of two bonded atoms that define
9997 a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
9998 (either a new IntGroup or 'group' with incremented reference count; thus the caller
9999 is responsible for releasing the returned value). */
10001 MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
10004 if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
10005 if (rotGroup != NULL) {
10006 IntGroupRetain(group);
10011 if (group != NULL && IntGroupGetCount(group) == 2) {
10012 i1 = IntGroupGetNthPoint(group, 0);
10013 i2 = IntGroupGetNthPoint(group, 1);
10014 if (MoleculeAreAtomsConnected(mp, i1, i2)) {
10015 IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
10018 i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
10020 IntGroupRelease(frag);
10021 if (rotGroup != NULL)
10025 if (rotGroup != NULL)
10027 else if (frag != NULL)
10028 IntGroupRelease(frag);
10035 #pragma mark ====== Multiple frame ======
10038 MoleculeGetNumberOfFrames(Molecule *mp)
10042 if (mp->nframes <= 0) {
10046 for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10047 if (ap->nframes > n)
10054 return mp->nframes;
10058 MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame, const Vector *inFrameCell)
10060 int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
10061 Vector *tempv, *vp;
10063 if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
10066 n_old = MoleculeGetNumberOfFrames(mp);
10067 n_new = n_old + count;
10068 last_inserted = IntGroupGetNthPoint(group, count - 1);
10069 if (n_new <= last_inserted) {
10070 exframes = last_inserted - n_new + 1; /* number of extra frames that will be silently inserted */
10072 } else exframes = 0;
10074 tempv = (Vector *)malloc(sizeof(Vector) * n_new * 4); /* "*4" for handling cells */
10078 __MoleculeLock(mp);
10080 /* Copy back the current coordinates */
10081 /* No change in the current coordinates, but the frame buffer is updated */
10082 MoleculeSelectFrame(mp, mp->cframe, 1);
10084 /* Expand ap->frames for all atoms */
10085 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10086 if (ap->frames == NULL)
10087 vp = (Vector *)calloc(sizeof(Vector), n_new);
10089 vp = (Vector *)realloc(ap->frames, sizeof(Vector) * n_new);
10091 __MoleculeUnlock(mp);
10094 for (j = ap->nframes; j < n_new; j++)
10098 if (mp->cell != NULL) {
10099 j = mp->nframe_cells;
10100 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, n_new - 1, NULL);
10101 for (i = j; i < n_new; i++) {
10102 /* Set the current cell parameters to the expanded frames */
10103 mp->frame_cells[i * 4] = mp->cell->axes[0];
10104 mp->frame_cells[i * 4 + 1] = mp->cell->axes[1];
10105 mp->frame_cells[i * 4 + 2] = mp->cell->axes[2];
10106 mp->frame_cells[i * 4 + 3] = mp->cell->origin;
10110 /* group = [n0..n1-1, n2..n3-1, ...] */
10112 /* tempv[0..n0-1] <- ap[0..n0-1], s += n0,
10113 tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
10114 tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
10115 tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
10117 tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
10118 At last, s will become n_old and t will become count. */
10119 for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10120 int s, t, ns, ne, mult;
10123 if (i == mp->natoms) {
10124 if (mp->cell == NULL || mp->frame_cells == NULL)
10126 vp = mp->frame_cells;
10133 for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10135 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (ns - ne));
10138 ne = IntGroupGetEndPoint(group, j);
10140 if (i == mp->natoms) {
10141 if (inFrameCell != NULL) {
10142 tempv[ns * 4] = inFrameCell[t * 4];
10143 tempv[ns * 4 + 1] = inFrameCell[t * 4 + 1];
10144 tempv[ns * 4 + 2] = inFrameCell[t * 4 + 2];
10145 tempv[ns * 4 + 3] = inFrameCell[t * 4 + 3];
10147 tempv[ns * 4] = mp->cell->axes[0];
10148 tempv[ns * 4 + 1] = mp->cell->axes[1];
10149 tempv[ns * 4 + 2] = mp->cell->axes[2];
10150 tempv[ns * 4 + 3] = mp->cell->origin;
10153 if (inFrame != NULL)
10154 tempv[ns] = inFrame[natoms * t + i];
10163 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (n_new - ne));
10166 if (i < mp->natoms)
10167 ap->nframes = n_new;
10168 memmove(vp, tempv, sizeof(Vector) * mult * n_new);
10171 mp->nframes = n_new;
10172 MoleculeSelectFrame(mp, last_inserted, 0);
10173 MoleculeIncrementModifyCount(mp);
10174 __MoleculeUnlock(mp);
10179 MoleculeRemoveFrames(Molecule *mp, IntGroup *inGroup, Vector *outFrame, Vector *outFrameCell)
10181 int i, count, n_new, n_old, natoms, nframes, old_count, new_cframe;
10182 Vector *tempv, *vp;
10184 IntGroup *group, *group2;
10186 if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(inGroup)) <= 0)
10189 /* outFrame[] should have enough size for Vector * natoms * group.count */
10190 memset(outFrame, 0, sizeof(Vector) * natoms * count);
10191 if (mp->cell != NULL && mp->frame_cells != NULL)
10192 memset(outFrameCell, 0, sizeof(Vector) * 4 * count);
10194 n_old = MoleculeGetNumberOfFrames(mp);
10196 return -2; /* Cannot delete last frame */
10198 group = IntGroupNew();
10199 group2 = IntGroupNewWithPoints(0, n_old, -1);
10200 IntGroupIntersect(inGroup, group2, group);
10201 IntGroupRelease(group2);
10202 count = IntGroupGetCount(group);
10203 n_new = n_old - count;
10205 IntGroupRelease(group);
10206 return -2; /* Trying to delete too many frames */
10208 tempv = (Vector *)malloc(sizeof(Vector) * n_old * 4); /* "*4" for handling cells */
10209 if (tempv == NULL) {
10210 IntGroupRelease(group);
10214 __MoleculeLock(mp);
10216 /* Copy back the current coordinates */
10217 /* No change in the current coordinates, but the frame buffer is updated */
10218 MoleculeSelectFrame(mp, mp->cframe, 1);
10220 /* Determine which frame should be selected after removal is completed */
10223 if (IntGroupLookup(group, mp->cframe, &i)) {
10224 /* cframe will be removed */
10225 n1 = IntGroupGetStartPoint(group, i) - 1;
10227 n1 = IntGroupGetEndPoint(group, i);
10228 } else n1 = mp->cframe;
10229 /* Change to that frame */
10230 MoleculeSelectFrame(mp, n1, 0);
10231 group2 = IntGroupNewFromIntGroup(group);
10232 IntGroupReverse(group2, 0, n_old);
10233 new_cframe = IntGroupLookupPoint(group2, n1);
10234 if (new_cframe < 0)
10235 return -3; /* This cannot happen */
10236 IntGroupRelease(group2);
10239 /* group = [n0..n1-1, n2..n3-1, ...] */
10241 /* tempv[0..n0-1] -> ap[0..n0-1], s += n0,
10242 tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
10243 tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
10244 tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
10246 tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
10247 At last, s will become n_new and t will become count. */
10249 for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10250 int s, t, j, ns, ne;
10252 /* if i == mp->natoms, mp->frame_cells is handled */
10253 if (i == mp->natoms) {
10254 if (mp->cell == NULL || mp->frame_cells == NULL)
10257 vp = mp->frame_cells;
10263 ap->frames = vp = (Vector *)calloc(sizeof(Vector), n_old);
10265 __MoleculeUnlock(mp);
10269 old_count = ap->nframes;
10272 /* Copy vp to tempv */
10273 memset(tempv, 0, sizeof(Vector) * mult * n_old);
10274 memmove(tempv, vp, sizeof(Vector) * mult * (old_count > n_old ? n_old : old_count));
10275 ne = ns = s = t = 0;
10276 for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10280 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (ns - ne));
10283 ne = IntGroupGetEndPoint(group, j);
10287 if (i < mp->natoms)
10288 outFrame[natoms * t + i] = tempv[ns];
10289 else if (outFrameCell != NULL) {
10290 outFrameCell[t * 4] = tempv[ns * 4];
10291 outFrameCell[t * 4 + 1] = tempv[ns * 4 + 1];
10292 outFrameCell[t * 4 + 2] = tempv[ns * 4 + 2];
10293 outFrameCell[t * 4 + 3] = tempv[ns * 4 + 3];
10300 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (n_old - ne));
10303 if (i < mp->natoms)
10308 if (i < mp->natoms) {
10313 free(mp->frame_cells);
10314 mp->frame_cells = NULL;
10315 mp->nframe_cells = 0;
10318 if (i < mp->natoms)
10319 ap->frames = (Vector *)realloc(ap->frames, sizeof(Vector) * s);
10321 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, s - 1, NULL);
10322 mp->nframe_cells = s;
10327 mp->nframes = nframes;
10329 /* Select the "last" frame; do not "copy back" the coordinates to the frame table */
10330 /* i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe); */
10331 MoleculeSelectFrame(mp, new_cframe, 0);
10333 IntGroupRelease(group);
10335 MoleculeIncrementModifyCount(mp);
10336 __MoleculeUnlock(mp);
10341 MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
10343 int i, cframe, nframes, modified;
10345 cframe = mp->cframe;
10346 nframes = MoleculeGetNumberOfFrames(mp);
10348 frame = mp->cframe;
10349 if (mp == NULL || mp->natoms == 0 || frame < 0 || frame >= nframes)
10352 __MoleculeLock(mp);
10353 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10354 if (copyback && cframe >= 0 && cframe < ap->nframes) {
10355 /* Write the current coordinate back to the frame array */
10356 ap->frames[cframe] = ap->r;
10358 if (frame != cframe && frame >= 0 && frame < ap->nframes) {
10359 /* Read the coordinate from the frame array */
10360 ap->r = ap->frames[frame];
10365 if (mp->cell != NULL && mp->frame_cells != NULL) {
10366 /* Write the current cell back to the frame_cells array */
10367 if (copyback && cframe >= 0) {
10368 Vector *vp = (Vector *)AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, cframe, NULL);
10369 vp[0] = mp->cell->axes[0];
10370 vp[1] = mp->cell->axes[1];
10371 vp[2] = mp->cell->axes[2];
10372 vp[3] = mp->cell->origin;
10374 /* Set the cell from the frame array */
10375 if (frame != cframe && frame >= 0 && frame < mp->nframe_cells) {
10376 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);
10378 MoleculeAmendBySymmetry(mp, NULL, NULL, NULL);
10381 mp->cframe = frame;
10383 mp->needsMDCopyCoordinates = 1;
10384 __MoleculeUnlock(mp);
10385 sMoleculeNotifyChangeAppearance(mp);
10389 /* If molecule is multi-frame, then flush the current information to the frame buffer.
10390 Returns the number of frames. */
10392 MoleculeFlushFrames(Molecule *mp)
10394 int nframes = MoleculeGetNumberOfFrames(mp);
10396 MoleculeSelectFrame(mp, mp->cframe, 1);
10400 #pragma mark ====== Pi Atoms ======
10403 sMoleculeCalculatePiAnchorPosition(Atom *ap, Atom *atoms)
10407 cp = AtomConnectData(&ap->anchor->connect);
10408 n = ap->anchor->connect.count;
10410 for (j = 0; j < n; j++) {
10411 Double w = ap->anchor->coeffs[j];
10412 ap2 = ATOM_AT_INDEX(atoms, cp[j]);
10413 VecScaleInc(ap->r, ap2->r, w);
10418 MoleculeUpdatePiAnchorPositions(Molecule *mol)
10422 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
10423 if (ap->anchor == NULL)
10425 sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
10430 MoleculeCalculatePiAnchorPosition(Molecule *mol, int idx)
10433 if (mol == NULL || idx < 0 || idx >= mol->natoms)
10435 ap = ATOM_AT_INDEX(mol->atoms, idx);
10436 if (ap->anchor == NULL)
10438 sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
10442 MoleculeSetPiAnchorList(Molecule *mol, Int idx, Int nentries, Int *entries, Double *weights, Int *nUndoActions, struct MolAction ***undoActions)
10445 Int *ip, i, j, n, *np;
10447 if (mol == NULL || idx < 0 || idx >= mol->natoms || nentries <= 1)
10448 return -1; /* Invalid argument */
10449 if (weights != NULL) {
10451 for (i = 0; i < nentries; i++) {
10452 if (weights[i] <= 0.0) {
10453 return 10; /* Weights must be positive */
10458 } else d = 1.0 / nentries;
10459 ap = ATOM_AT_INDEX(mol->atoms, idx);
10460 if (ap->anchor != NULL) {
10461 /* Already an anchor: check if bonds/angles/dihedrals have entries related to this anchor */
10462 IntGroup *bg, *ag, *dg, *ig;
10463 Int *ibuf, ibufsize;
10465 bg = ag = dg = ig = NULL;
10466 ip = AtomConnectData(&ap->anchor->connect);
10467 for (i = 0; i < ap->anchor->connect.count; i++) {
10469 for (j = 0; j < nentries; j++) {
10470 if (n == entries[j])
10473 if (j == nentries) {
10474 /* This entry will disappear: if any bond/angle/dihedral has idx-n pair, that should be removed. */
10475 for (j = 0, np = mol->bonds; j < mol->nbonds; j++, np += 2) {
10476 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[0])) {
10478 bg = IntGroupNew();
10479 IntGroupAdd(bg, j, 1);
10482 for (j = 0, np = mol->angles; j < mol->nangles; j++, np += 3) {
10483 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) ||
10484 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1])) {
10486 ag = IntGroupNew();
10487 IntGroupAdd(ag, j, 1);
10490 for (j = 0, np = mol->dihedrals; j < mol->ndihedrals; j++, np += 4) {
10491 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) || (idx == np[2] && n == np[3]) ||
10492 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[3] && n == np[2])) {
10494 dg = IntGroupNew();
10495 IntGroupAdd(dg, j, 1);
10498 for (j = 0, np = mol->impropers; j < mol->nimpropers; j++, np += 4) {
10499 if ((idx == np[0] && n == np[2]) || (idx == np[1] && n == np[2]) || (idx == np[3] && n == np[2]) ||
10500 (idx == np[2] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[2] && n == np[3])) {
10502 ig = IntGroupNew();
10503 IntGroupAdd(ig, j, 1);
10511 /* Delete impropers (with undo info) */
10512 i = IntGroupGetCount(ig);
10513 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
10514 MoleculeDeleteImpropers(mol, ibuf, ig);
10515 if (nUndoActions != NULL && undoActions != NULL) {
10516 act = MolActionNew(gMolActionAddImpropers, i * 4, ibuf, ig);
10517 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10519 IntGroupRelease(ig);
10522 /* Delete dihedrals (with undo info) */
10523 i = IntGroupGetCount(dg);
10524 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
10525 MoleculeDeleteDihedrals(mol, ibuf, dg);
10526 if (nUndoActions != NULL && undoActions != NULL) {
10527 act = MolActionNew(gMolActionAddDihedrals, i * 4, ibuf, dg);
10528 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10530 IntGroupRelease(dg);
10533 /* Delete angles (with undo info) */
10534 i = IntGroupGetCount(ag);
10535 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 3 - 1, NULL);
10536 MoleculeDeleteAngles(mol, ibuf, ag);
10537 if (nUndoActions != NULL && undoActions != NULL) {
10538 act = MolActionNew(gMolActionAddAngles, i * 3, ibuf, ag);
10539 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10541 IntGroupRelease(ag);
10544 /* Delete bonds (with undo info) */
10545 i = IntGroupGetCount(bg);
10546 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 2 - 1, NULL);
10547 MoleculeDeleteBonds(mol, ibuf, bg, NULL, NULL);
10548 if (nUndoActions != NULL && undoActions != NULL) {
10549 act = MolActionNew(gMolActionAddBondsForUndo, i * 2, ibuf, bg);
10550 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10552 IntGroupRelease(bg);
10555 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
10557 AtomConnectResize(&ap->anchor->connect, nentries);
10558 memmove(AtomConnectData(&ap->anchor->connect), entries, sizeof(Int) * nentries);
10559 AssignArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), nentries - 1, NULL);
10560 if (weights != NULL) {
10561 memmove(ap->anchor->coeffs, weights, sizeof(Double) * nentries);
10562 for (i = 0; i < nentries; i++)
10563 ap->anchor->coeffs[i] *= d; /* Normalize weight */
10565 for (i = 0; i < nentries; i++)
10566 ap->anchor->coeffs[i] = d;
10568 MoleculeCalculatePiAnchorPosition(mol, idx);
10572 #pragma mark ====== MO calculation ======
10574 /* Calculate an MO value for a single point. */
10575 /* Index is the MO number (1-based) */
10576 /* tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom. */
10578 sCalcMOPoint(Molecule *mp, const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
10582 Double val, tval, *cnp, *tmpp, *mobasep, *mop;
10584 /* Cache dr and |dr|^2 */
10585 for (i = 0; i < mp->natoms; i++) {
10587 r = ATOM_AT_INDEX(mp->atoms, i)->r;
10588 tmp[i * 4] = r.x = (vp->x - r.x) * kAngstrom2Bohr;
10589 tmp[i * 4 + 1] = r.y = (vp->y - r.y) * kAngstrom2Bohr;
10590 tmp[i * 4 + 2] = r.z = (vp->z - r.z) * kAngstrom2Bohr;
10591 tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
10593 /* Iterate over all shells */
10595 mobasep = bset->mo + (index - 1) * bset->ncomps;
10596 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
10597 pp = bset->priminfos + sp->p_idx;
10598 cnp = bset->cns + sp->cn_idx;
10599 if (sp->a_idx >= mp->natoms)
10600 return 0.0; /* This may happen when molecule is edited after setting up MO info */
10601 tmpp = tmp + sp->a_idx * 4;
10602 mop = mobasep + sp->m_idx;
10606 for (j = 0; j < sp->nprim; j++) {
10607 tval += *cnp++ * exp(-pp->A * tmpp[3]);
10610 val += mop[0] * tval;
10616 for (j = 0; j < sp->nprim; j++) {
10617 tval = exp(-pp->A * tmpp[3]);
10618 x += *cnp++ * tval;
10619 y += *cnp++ * tval;
10620 z += *cnp++ * tval;
10623 x *= mop[0] * tmpp[0];
10624 y *= mop[1] * tmpp[1];
10625 z *= mop[2] * tmpp[2];
10629 case kGTOType_SP: {
10632 for (j = 0; j < sp->nprim; j++) {
10633 tval = exp(-pp->A * tmpp[3]);
10634 t += *cnp++ * tval;
10635 x += *cnp++ * tval;
10636 y += *cnp++ * tval;
10637 z += *cnp++ * tval;
10641 x *= mop[1] * tmpp[0];
10642 y *= mop[2] * tmpp[1];
10643 z *= mop[3] * tmpp[2];
10644 val += t + x + y + z;
10648 Double xx, yy, zz, xy, xz, yz;
10649 xx = yy = zz = xy = xz = yz = 0;
10650 for (j = 0; j < sp->nprim; j++) {
10651 tval = exp(-pp->A * tmpp[3]);
10652 xx += *cnp++ * tval;
10653 yy += *cnp++ * tval;
10654 zz += *cnp++ * tval;
10655 xy += *cnp++ * tval;
10656 xz += *cnp++ * tval;
10657 yz += *cnp++ * tval;
10660 xx *= mop[0] * tmpp[0] * tmpp[0];
10661 yy *= mop[1] * tmpp[1] * tmpp[1];
10662 zz *= mop[2] * tmpp[2] * tmpp[2];
10663 xy *= mop[3] * tmpp[0] * tmpp[1];
10664 xz *= mop[4] * tmpp[0] * tmpp[2];
10665 yz *= mop[5] * tmpp[1] * tmpp[2];
10666 val += xx + yy + zz + xy + xz + yz;
10669 case kGTOType_D5: {
10670 Double d0, d1p, d1n, d2p, d2n;
10671 d0 = d1p = d1n = d2p = d2n = 0;
10672 for (j = 0; j < sp->nprim; j++) {
10673 tval = exp(-pp->A * tmpp[3]);
10674 d0 += *cnp++ * tval;
10675 d1p += *cnp++ * tval;
10676 d1n += *cnp++ * tval;
10677 d2p += *cnp++ * tval;
10678 d2n += *cnp++ * tval;
10681 d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
10682 d1p *= mop[1] * tmpp[0] * tmpp[2];
10683 d1n *= mop[2] * tmpp[1] * tmpp[2];
10684 d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
10685 d2n *= mop[4] * tmpp[0] * tmpp[1];
10686 val += d0 + d1p + d1n + d2p + d2n;
10689 /* TODO: Support F/F7 and G/G9 type orbitals */
10695 /* Calculate one MO. The input vectors are angstrom unit (changed from bohr unit: 20140520) */
10696 /* mono is the MO number (1-based) */
10698 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)
10700 int ix, iy, iz, n, nn;
10703 if (mp == NULL || mp->bset == NULL)
10705 if (mp->bset->cns == NULL) {
10706 if (sSetupGaussianCoefficients(mp->bset) != 0)
10709 if (mp->bset->natoms_bs > mp->natoms)
10710 return -3; /* Number of atoms is smaller than expected (internal error) */
10712 cp = (Cube *)calloc(sizeof(Cube), 1);
10716 cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
10717 if (cp->dp == NULL) {
10730 /* TODO: use multithread */
10731 tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms_bs * 4);
10738 for (ix = 0; ix < nx; ix++) {
10740 for (iy = 0; iy < ny; iy++) {
10741 for (iz = 0; iz < nz; iz++) {
10742 p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
10743 p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
10744 p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
10745 cp->dp[n++] = sCalcMOPoint(mp, mp->bset, mono, &p, tmp);
10747 if (callback != NULL && n - nn > 100) {
10749 if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
10753 return -2; /* User interrupt */
10760 AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
10761 return mp->bset->ncubes - 1;
10764 /* Output values are in angstrom unit (changed from bohr unit: 20140520) */
10766 MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
10769 Vector rmin, rmax, r;
10770 Double dr, dx, dy, dz;
10772 if (mp == NULL || mp->bset == NULL)
10776 rmin.x = rmin.y = rmin.z = 1e10;
10777 rmax.x = rmax.y = rmax.z = -1e10;
10778 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10779 dr = RadiusForAtomicNumber(ap->atomicNumber);
10783 dr = dr * 3.0 + 2.0;
10784 if (rmin.x > r.x - dr)
10786 if (rmin.y > r.y - dr)
10788 if (rmin.z > r.z - dr)
10790 if (rmax.x < r.x + dr)
10792 if (rmax.y < r.y + dr)
10794 if (rmax.z < r.z + dr)
10797 dx = rmax.x - rmin.x;
10798 dy = rmax.y - rmin.y;
10799 dz = rmax.z - rmin.z;
10800 dr = pow(dx * dy * dz / npoints, 1.0/3.0);
10801 *nx = floor(dx / dr + 0.5);
10802 *ny = floor(dy / dr + 0.5);
10803 *nz = floor(dz / dr + 0.5);
10811 xp->x = yp->y = zp->z = dr;
10812 xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
10817 MoleculeGetCubeAtIndex(Molecule *mp, Int index)
10819 if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
10821 return mp->bset->cubes[index];
10825 MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
10828 if (mp == NULL || mp->bset == NULL)
10830 for (i = 0; i < mp->bset->ncubes; i++) {
10831 if (mp->bset->cubes[i]->idn == mono)
10838 MoleculeClearCubeAtIndex(Molecule *mp, Int index)
10841 if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
10843 CubeRelease(mp->bset->cubes[index]);
10845 memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
10846 if (--(mp->bset->ncubes) == 0) {
10847 free(mp->bset->cubes);
10848 mp->bset->cubes = NULL;
10850 return mp->bset->ncubes;
10854 MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
10859 if (mp == NULL || mp->bset == NULL)
10860 return -1; /* Molecule or the basis set information is empty */
10861 cp = MoleculeGetCubeAtIndex(mp, index);
10863 return -2; /* MO not yet calculated */
10864 fp = fopen(fname, "wb");
10866 return -3; /* Cannot create file */
10868 /* Comment lines */
10869 fprintf(fp, "%s MO=%d\n", comment, cp->idn);
10870 fprintf(fp, " MO coefficients\n");
10872 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms_bs),
10873 cp->origin.x * kAngstrom2Bohr, cp->origin.y * kAngstrom2Bohr, cp->origin.z * kAngstrom2Bohr);
10874 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx,
10875 cp->dx.x * kAngstrom2Bohr, cp->dx.y * kAngstrom2Bohr, cp->dx.z * kAngstrom2Bohr);
10876 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny,
10877 cp->dy.x * kAngstrom2Bohr, cp->dy.y * kAngstrom2Bohr, cp->dy.z * kAngstrom2Bohr);
10878 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz,
10879 cp->dz.x * kAngstrom2Bohr, cp->dz.y * kAngstrom2Bohr, cp->dz.z * kAngstrom2Bohr);
10881 /* Atomic information */
10882 for (i = 0; i < mp->natoms; i++) {
10883 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
10884 /* The second number should actually be the effective charge */
10885 fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber,
10886 ap->r.x * kAngstrom2Bohr, ap->r.y * kAngstrom2Bohr, ap->r.z * kAngstrom2Bohr);
10888 fprintf(fp, "%5d%5d\n", 1, 1);
10891 for (i = n = 0; i < cp->nx; i++) {
10892 for (j = 0; j < cp->ny; j++) {
10893 for (k = 0; k < cp->nz; k++) {
10894 /* On Windows, the "%e" format writes the exponent in 3 digits, but
10895 this is not standard. So we avoid using %e */
10896 Double d = cp->dp[n++];
10897 int exponent = (int)floor(log10(fabs(d)));
10898 Double base = d * pow(10, -1.0 * exponent);
10899 fprintf(fp, " %8.5fe%+03d", base, exponent);
10900 /* fprintf(fp, " %12.5e", d); */
10901 if (k == cp->nz - 1 || k % 6 == 5)
10910 #pragma mark ====== Marching Cube (for isosurface) ======
10913 MoleculeClearMCube(Molecule *mol, Int nx, Int ny, Int nz, const Vector *origin, Double dx, Double dy, Double dz)
10915 MCube *mc = mol->mcube;
10917 float rgba[8] = { 1, 1, 1, 0.6, 0, 0, 1, 0.6 };
10922 free(mc->c[0].cubepoints);
10923 free(mc->c[0].triangles);
10925 free(mc->c[1].cubepoints);
10926 free(mc->c[1].triangles);
10927 memmove(rgba, mc->c[0].rgba, sizeof(float) * 4);
10928 memmove(rgba + 4, mc->c[1].rgba, sizeof(float) * 4);
10932 if (nx > 0 && ny > 0 && nz > 0) {
10933 mc = (MCube *)calloc(sizeof(MCube), 1);
10934 /* round up to nearest 4N+1 integer */
10938 mc->nx = (nx + 2) / 4 * 4 + 1;
10939 mc->ny = (ny + 2) / 4 * 4 + 1;
10940 mc->nz = (nz + 2) / 4 * 4 + 1;
10941 mc->dx = dx / mc->nx;
10942 mc->dy = dy / mc->ny;
10943 mc->dz = dz / mc->nz;
10944 mc->origin = *origin;
10945 mc->dp = (Double *)malloc(sizeof(Double) * mc->nx * mc->ny * mc->nz);
10946 if (mc->dp == NULL) {
10950 mc->c[0].fp = (unsigned char *)calloc(sizeof(unsigned char), mc->nx * mc->ny * mc->nz);
10951 mc->c[1].fp = (unsigned char *)calloc(sizeof(unsigned char), mc->nx * mc->ny * mc->nz);
10952 if (mc->c[0].fp == NULL || mc->c[1].fp == NULL) {
10959 for (i = 0; i < mc->nx * mc->ny * mc->nz; i++) {
10960 mc->dp[i] = DBL_MAX;
10962 memmove(mc->c[0].rgba, rgba, sizeof(float) * 4);
10963 memmove(mc->c[1].rgba, rgba + 4, sizeof(float) * 4);
10966 MoleculeCallback_notifyModification(mol, 0);
10970 static int sMarchingCubeTable[256][16] = {
10971 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10972 {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10973 {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10974 {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10975 {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10976 {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10977 {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10978 {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1},
10979 {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10980 {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10981 {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10982 {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1},
10983 {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10984 {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1},
10985 {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1},
10986 {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10987 {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10988 {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10989 {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10990 {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1},
10991 {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10992 {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1},
10993 {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
10994 {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1},
10995 {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10996 {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1},
10997 {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
10998 {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1},
10999 {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1},
11000 {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1},
11001 {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1},
11002 {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1},
11003 {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11004 {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11005 {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11006 {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1},
11007 {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11008 {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
11009 {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1},
11010 {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1},
11011 {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11012 {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
11013 {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
11014 {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1},
11015 {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1},
11016 {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1},
11017 {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1},
11018 {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1},
11019 {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11020 {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
11021 {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1},
11022 {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11023 {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1},
11024 {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1},
11025 {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1},
11026 {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1},
11027 {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
11028 {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1},
11029 {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1},
11030 {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1},
11031 {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1},
11032 {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1},
11033 {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1},
11034 {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11035 {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11036 {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11037 {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11038 {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
11039 {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11040 {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1},
11041 {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1},
11042 {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1},
11043 {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11044 {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
11045 {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
11046 {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1},
11047 {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1},
11048 {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1},
11049 {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1},
11050 {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1},
11051 {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11052 {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1},
11053 {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
11054 {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1},
11055 {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1},
11056 {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1},
11057 {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1},
11058 {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1},
11059 {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
11060 {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1},
11061 {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1},
11062 {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1},
11063 {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1},
11064 {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1},
11065 {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1},
11066 {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1},
11067 {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11068 {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1},
11069 {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
11070 {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1},
11071 {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1},
11072 {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1},
11073 {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11074 {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1},
11075 {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1},
11076 {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1},
11077 {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1},
11078 {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1},
11079 {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1},
11080 {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1},
11081 {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1},
11082 {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11083 {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1},
11084 {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1},
11085 {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1},
11086 {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1},
11087 {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1},
11088 {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1},
11089 {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1},
11090 {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11091 {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1},
11092 {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1},
11093 {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1},
11094 {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1},
11095 {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1},
11096 {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11097 {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1},
11098 {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11099 {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11100 {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11101 {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11102 {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
11103 {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11104 {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
11105 {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
11106 {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1},
11107 {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11108 {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1},
11109 {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1},
11110 {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1},
11111 {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1},
11112 {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1},
11113 {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1},
11114 {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1},
11115 {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11116 {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1},
11117 {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1},
11118 {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1},
11119 {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1},
11120 {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1},
11121 {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1},
11122 {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1},
11123 {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1},
11124 {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11125 {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1},
11126 {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1},
11127 {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1},
11128 {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1},
11129 {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1},
11130 {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11131 {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11132 {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
11133 {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
11134 {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1},
11135 {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
11136 {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1},
11137 {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1},
11138 {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1},
11139 {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
11140 {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1},
11141 {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1},
11142 {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1},
11143 {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1},
11144 {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1},
11145 {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1},
11146 {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1},
11147 {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1},
11148 {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1},
11149 {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1},
11150 {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1},
11151 {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1},
11152 {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1},
11153 {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1},
11154 {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1},
11155 {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1},
11156 {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1},
11157 {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1},
11158 {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11159 {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1},
11160 {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1},
11161 {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11162 {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11163 {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11164 {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1},
11165 {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1},
11166 {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1},
11167 {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1},
11168 {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1},
11169 {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1},
11170 {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1},
11171 {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1},
11172 {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1},
11173 {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1},
11174 {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1},
11175 {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11176 {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1},
11177 {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1},
11178 {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11179 {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1},
11180 {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1},
11181 {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1},
11182 {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1},
11183 {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1},
11184 {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1},
11185 {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1},
11186 {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11187 {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1},
11188 {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1},
11189 {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1},
11190 {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1},
11191 {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1},
11192 {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11193 {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1},
11194 {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11195 {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1},
11196 {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1},
11197 {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1},
11198 {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1},
11199 {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1},
11200 {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1},
11201 {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1},
11202 {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1},
11203 {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1},
11204 {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1},
11205 {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1},
11206 {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11207 {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1},
11208 {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1},
11209 {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11210 {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11211 {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11212 {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1},
11213 {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1},
11214 {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11215 {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1},
11216 {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1},
11217 {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11218 {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11219 {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1},
11220 {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11221 {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1},
11222 {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11223 {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11224 {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11225 {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11226 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
11229 /* Recalculate the MCube */
11230 /* If idn < 0, then the current grid settings and values are unchanged, and */
11231 /* only the marching cubes are regenerated. */
11233 MoleculeUpdateMCube(Molecule *mol, int idn)
11235 Int flags, retval, step, hstep, sn;
11236 Int n, ix, iy, iz, nx, ny, nz;
11237 Int nn, iix, iiy, iiz;
11238 Int ncubepoints, c1, c2, c3;
11240 Double thres, *tmp, dd;
11246 if (mol == NULL || mol->bset == NULL || mol->mcube == NULL)
11248 if (mol->bset->cns == NULL) {
11249 if (sSetupGaussianCoefficients(mol->bset) != 0)
11252 if (mol->bset->natoms_bs > mol->natoms)
11253 return -1; /* Number of atoms is smaller than expected */
11258 Double *mobasep, *mop, mopmax;
11259 Double xmin, xmax, ymin, ymax, zmin, zmax;
11260 /* Clear mcube values */
11261 for (ix = 0; ix < mc->nx * mc->ny * mc->nz; ix++) {
11262 mc->dp[ix] = DBL_MAX;
11263 mc->c[0].fp[ix] = 0;
11264 mc->c[1].fp[ix] = 0;
11267 /* Estimate the orbital sizes */
11268 mc->radii = (Double *)realloc(mc->radii, sizeof(Double) * mol->natoms);
11269 if (mc->radii == NULL)
11270 return -2; /* Out of memory */
11271 mc->nradii = mol->natoms;
11272 memset(mc->radii, 0, sizeof(Double) * mc->nradii);
11273 mobasep = mol->bset->mo + (mc->idn - 1) * mol->bset->ncomps;
11275 for (ix = 0, sp = mol->bset->shells; ix < mol->bset->nshells; ix++, sp++) {
11276 if (sp->a_idx >= mol->natoms)
11277 continue; /* This may happen when molecule is edited after setting up MO info */
11278 mop = mobasep + sp->m_idx;
11279 for (iy = 0; iy < sp->ncomp; iy++) {
11280 dd = fabs(mop[iy]);
11281 if (dd > mc->radii[sp->a_idx])
11282 mc->radii[sp->a_idx] = dd;
11287 xmin = ymin = zmin = 1e10;
11288 xmax = ymax = zmax = -1e10;
11289 for (ix = 0, ap = mol->atoms; ix < mol->natoms; ix++, ap = ATOM_NEXT(ap)) {
11290 dd = RadiusForAtomicNumber(ap->atomicNumber);
11291 dd = (dd * 2.0 + 1.0) * (mc->radii[ix] / mopmax) * (mc->expand > 0.0 ? mc->expand : 1.0);
11292 mc->radii[ix] = dd;
11295 if (p.x - dd < xmin)
11297 if (p.y - dd < ymin)
11299 if (p.z - dd < zmin)
11301 if (p.x + dd > xmax)
11303 if (p.y + dd > ymax)
11305 if (p.z + dd > zmax)
11308 mc->origin.x = xmin;
11309 mc->origin.y = ymin;
11310 mc->origin.z = zmin;
11311 mc->dx = (xmax - xmin) / mc->nx;
11312 mc->dy = (ymax - ymin) / mc->ny;
11313 mc->dz = (zmax - zmin) / mc->nz;
11316 /* Temporary work area */
11317 tmp = (Double *)calloc(sizeof(Double), mol->bset->natoms_bs * 4);
11321 /* TODO: use multithread */
11328 /* Calculate points within certain distances from atoms */
11329 for (nn = 0, ap = mol->atoms; nn < mol->natoms; nn++, ap = ATOM_NEXT(ap)) {
11330 /* dd = RadiusForAtomicNumber(ap->atomicNumber);
11333 dd = dd * 1.5 + 1.0; */
11334 dd = mc->radii[nn];
11335 p.x = ap->r.x - dd - mc->origin.x;
11336 p.y = ap->r.y - dd - mc->origin.y;
11337 p.z = ap->r.z - dd - mc->origin.z;
11341 iix = c1 + ceil(dd * 2.0 / mc->dx);
11342 iiy = c2 + ceil(dd * 2.0 / mc->dy);
11343 iiz = c3 + ceil(dd * 2.0 / mc->dz);
11356 for (ix = c1; ix <= iix; ix++) {
11357 p.x = mc->origin.x + mc->dx * ix;
11358 for (iy = c2; iy <= iiy; iy++) {
11359 p.y = mc->origin.y + mc->dy * iy;
11360 for (iz = c3; iz <= iiz; iz++) {
11361 n = (ix * ny + iy) * nz + iz;
11362 if (mc->dp[n] == DBL_MAX) {
11363 p.z = mc->origin.z + mc->dz * iz;
11364 mc->dp[n] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
11372 /* (i * step, j * step, k * step) */
11373 for (ix = 0; ix < nx; ix += step) {
11374 for (iy = 0; iy < ny; iy += step) {
11375 for (iz = 0; iz < nz; iz += step) {
11376 n = (ix * ny + iy) * nz + iz;
11377 if (mc->dp[n] == DBL_MAX) {
11378 p.x = mc->origin.x + mc->dx * ix;
11379 p.y = mc->origin.y + mc->dy * iy;
11380 p.z = mc->origin.z + mc->dz * iz;
11381 mc->dp[n] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
11388 /* Intermediate points */
11389 for (step = 4; step > 1; step /= 2) {
11391 for (sn = 0; sn <= 1; sn++) {
11393 for (ix = 0; ix < nx - 1; ix += step) {
11394 for (iy = 0; iy < ny - 1; iy += step) {
11395 for (iz = 0; iz < nz - 1; iz += step) {
11397 thres = mc->thres * (sn == 0 ? 1 : -1);
11398 n = (ix * ny + iy) * nz + iz;
11399 if (mc->dp[n] == DBL_MAX || mc->dp[n + step * (nz * (ny + 1) + 1)] == DBL_MAX)
11402 if (mc->dp[n] >= thres)
11404 /* (ix + step, iy, iz) */
11405 if (mc->dp[n + step * ny * nz] >= thres)
11407 /* (ix, iy + step, iz) */
11408 if (mc->dp[n + step * nz] >= thres)
11410 /* (ix + 4, iy + step, iz) */
11411 if (mc->dp[n + step * nz * (ny + 1)] >= thres)
11413 /* (ix, iy, iz + step) */
11414 if (mc->dp[n + step] >= thres)
11416 if (mc->dp[n + step * (ny * nz + 1)] >= thres)
11418 /* (ix, iy + step, iz + step) */
11419 if (mc->dp[n + step * (nz + 1)] >= thres)
11421 /* (ix + step, iy + step, iz + step) */
11422 if (mc->dp[n + step * (nz * (ny + 1) + 1)] >= thres)
11424 if (flags != 0 && flags != 255) {
11425 /* Calc the intermediate points */
11426 for (iix = 0; iix <= step; iix += hstep) {
11427 for (iiy = 0; iiy <= step; iiy += hstep) {
11428 for (iiz = 0; iiz <= step; iiz += hstep) {
11429 if (iix % step == 0 && iiy % step == 0 && iiz % step == 0)
11431 nn = n + (iix * ny + iiy) * nz + iiz;
11432 if (mc->dp[nn] == DBL_MAX) {
11433 p.x = mc->origin.x + mc->dx * (ix + iix);
11434 p.y = mc->origin.y + mc->dy * (iy + iiy);
11435 p.z = mc->origin.z + mc->dz * (iz + iiz);
11436 mc->dp[nn] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
11452 /* Calculate vertex positions and normal vectors */
11453 for (sn = 0; sn <= 1; sn++) {
11455 thres = mc->thres * (sn == 0 ? 1 : -1);
11457 for (ix = 0; ix < nx - 1; ix++) {
11458 for (iy = 0; iy < ny - 1; iy++) {
11459 for (iz = 0; iz < nz - 1; iz++) {
11461 nn = (ix * ny + iy) * nz + iz;
11463 if (dd0 == DBL_MAX)
11466 dd1 = mc->dp[nn + ny * nz];
11467 if (dd1 != DBL_MAX)
11468 p.x = (dd1 - dd0) / mc->dx;
11469 else if (ix > 0 && (dd1 = mc->dp[nn - ny * nz]) != DBL_MAX)
11470 p.x = (dd0 - dd1) / mc->dx;
11471 else continue; /* Cannot define gradient */
11472 dd1 = mc->dp[nn + nz];
11473 if (dd1 != DBL_MAX)
11474 p.y = (dd1 - dd0) / mc->dy;
11475 else if (iy > 0 && (dd1 = mc->dp[nn - nz]) != DBL_MAX)
11476 p.y = (dd0 - dd1) / mc->dy;
11478 dd1 = mc->dp[nn + 1];
11479 if (dd1 != DBL_MAX)
11480 p.z = (dd1 - dd0) / mc->dz;
11481 else if (iz > 0 && (dd1 = mc->dp[nn - 1]) != DBL_MAX)
11482 p.z = (dd0 - dd1) / mc->dz;
11484 NormalizeVec(&p, &p);
11486 if (n + 3 >= mc->c[sn].ncubepoints) {
11487 /* Expand cubepoints[] array */
11488 mc->c[sn].cubepoints = (MCubePoint *)realloc(mc->c[sn].cubepoints, sizeof(MCubePoint) * (mc->c[sn].ncubepoints + 8192));
11489 if (mc->c[sn].cubepoints == NULL) {
11490 mc->c[sn].ncubepoints = 0;
11494 mc->c[sn].ncubepoints += 8192;
11496 mcp = mc->c[sn].cubepoints + n;
11497 iix = (dd0 >= thres ? 1 : -1);
11498 /* (x, y, z)->(x + 1, y, z) */
11499 dd1 = mc->dp[nn + ny * nz];
11500 if (dd1 != DBL_MAX) {
11501 iiy = (dd1 >= thres ? 1 : -1);
11505 mcp->d = (thres - dd0) / (dd1 - dd0);
11506 mcp->pos[0] = mc->origin.x + mc->dx * (ix + mcp->d);
11507 mcp->pos[1] = mc->origin.y + mc->dy * iy;
11508 mcp->pos[2] = mc->origin.z + mc->dz * iz;
11509 mcp->grad[0] = p.x;
11510 mcp->grad[1] = p.y;
11511 mcp->grad[2] = p.z;
11516 /* (x, y, z)->(x, y + 1, z) */
11517 dd1 = mc->dp[nn + nz];
11518 if (dd1 != DBL_MAX) {
11519 iiy = (dd1 >= thres ? 1 : -1);
11522 mcp->key = nn * 3 + 1;
11523 mcp->d = (thres - dd0) / (dd1 - dd0);
11524 mcp->pos[0] = mc->origin.x + mc->dx * ix;
11525 mcp->pos[1] = mc->origin.y + mc->dy * (iy + mcp->d);
11526 mcp->pos[2] = mc->origin.z + mc->dz * iz;
11527 mcp->grad[0] = p.x;
11528 mcp->grad[1] = p.y;
11529 mcp->grad[2] = p.z;
11534 /* (x, y, z)->(x, y, z + 1) */
11535 dd1 = mc->dp[nn + 1];
11536 if (dd1 != DBL_MAX) {
11537 iiy = (dd1 >= thres ? 1 : -1);
11540 mcp->key = nn * 3 + 2;
11541 mcp->d = (thres - dd0) / (dd1 - dd0);
11542 mcp->pos[0] = mc->origin.x + mc->dx * ix;
11543 mcp->pos[1] = mc->origin.y + mc->dy * iy;
11544 mcp->pos[2] = mc->origin.z + mc->dz * (iz + mcp->d);
11545 mcp->grad[0] = p.x;
11546 mcp->grad[1] = p.y;
11547 mcp->grad[2] = p.z;
11555 if (n < mc->c[sn].ncubepoints)
11556 mc->c[sn].cubepoints[n].key = -1; /* End mark */
11558 if (ncubepoints < 3) {
11559 /* Less than 3 points: no triangles */
11560 if (mc->c[sn].ntriangles > 0)
11561 mc->c[sn].triangles[0] = -1; /* End mark */
11566 /* Create triangle table */
11568 for (ix = 0; ix < nx - 1; ix++) {
11569 for (iy = 0; iy < ny - 1; iy++) {
11570 for (iz = 0; iz < nz - 1; iz++) {
11571 nn = (ix * ny + iy) * nz + iz;
11573 if ((dd = mc->dp[nn]) == DBL_MAX)
11575 else if (dd >= thres)
11577 if ((dd = mc->dp[nn + ny * nz]) == DBL_MAX)
11579 else if (dd >= thres)
11581 if ((dd = mc->dp[nn + ny * nz + nz]) == DBL_MAX)
11583 else if (dd >= thres)
11585 if ((dd = mc->dp[nn + nz]) == DBL_MAX)
11587 else if (dd >= thres)
11589 if ((dd = mc->dp[nn + 1]) == DBL_MAX)
11591 else if (dd >= thres)
11593 if ((dd = mc->dp[nn + ny * nz + 1]) == DBL_MAX)
11595 else if (dd >= thres)
11597 if ((dd = mc->dp[nn + ny * nz + nz + 1]) == DBL_MAX)
11599 else if (dd >= thres)
11601 if ((dd = mc->dp[nn + nz + 1]) == DBL_MAX)
11603 else if (dd >= thres)
11605 for (iiy = 0; iiy < 15; iiy++) {
11606 nn = sMarchingCubeTable[iix][iiy];
11609 /* key index for edges 0-11 */
11611 case 0: iiz = (( ix * ny + iy ) * nz + iz ) * 3; break;
11612 case 1: iiz = (((ix + 1) * ny + iy ) * nz + iz ) * 3 + 1; break;
11613 case 2: iiz = (( ix * ny + iy + 1) * nz + iz ) * 3; break;
11614 case 3: iiz = (( ix * ny + iy ) * nz + iz ) * 3 + 1; break;
11615 case 4: iiz = (( ix * ny + iy ) * nz + iz + 1) * 3; break;
11616 case 5: iiz = (((ix + 1) * ny + iy ) * nz + iz + 1) * 3 + 1; break;
11617 case 6: iiz = (( ix * ny + iy + 1) * nz + iz + 1) * 3; break;
11618 case 7: iiz = (( ix * ny + iy ) * nz + iz + 1) * 3 + 1; break;
11619 case 8: iiz = (( ix * ny + iy ) * nz + iz ) * 3 + 2; break;
11620 case 9: iiz = (((ix + 1) * ny + iy ) * nz + iz ) * 3 + 2; break;
11621 case 10: iiz = (((ix + 1) * ny + iy + 1) * nz + iz ) * 3 + 2; break;
11622 case 11: iiz = (( ix * ny + iy + 1) * nz + iz ) * 3 + 2; break;
11624 /* Skip this triangle */
11625 iiy = (iiy - iiy % 3) + 2;
11629 /* Look for the key index in cubepoints */
11631 c3 = ncubepoints - 1;
11632 mcp = mc->c[sn].cubepoints;
11635 /* c1 is always less than c3 */
11636 if (c1 + 1 == c3) {
11637 /* end of search */
11638 if (mcp[c1].key == iiz) {
11640 } else if (mcp[c3].key == iiz) {
11647 c2 = (c1 + c3) / 2;
11648 w = mcp[c2].key - iiz;
11658 /* Not found: skip this triangle */
11659 iiy = (iiy - iiy % 3) + 2;
11663 if (n + 1 >= mc->c[sn].ntriangles) {
11664 /* Expand triangles[] array */
11665 mc->c[sn].triangles = (Int *)realloc(mc->c[sn].triangles, sizeof(Int) * (mc->c[sn].ntriangles + 8192));
11666 if (mc->c[sn].triangles == NULL) {
11667 mc->c[sn].ntriangles = 0;
11671 mc->c[sn].ntriangles += 8192;
11673 mc->c[sn].triangles[n] = c2;
11679 if (n < mc->c[sn].ntriangles)
11680 mc->c[sn].triangles[n] = -1; /* End mark */
11682 /* Estimate the normal vector */
11683 for (n = 0, ip = mc->c[sn].triangles; ip[n] >= 0; n += 3) {
11685 for (ix = 0; ix < 3; ix++) {
11686 mcp = &(mc->c[sn].cubepoints[ip[n + ix]]);
11687 v[ix].x = mcp->pos[0];
11688 v[ix].y = mcp->pos[1];
11689 v[ix].z = mcp->pos[2];
11691 VecDec(v[2], v[0]);
11692 VecDec(v[1], v[0]);
11693 VecCross(v[0], v[1], v[2]);
11694 NormalizeVec(v, v);
11695 for (ix = 0; ix < 3; ix++) {
11696 mcp = &(mc->c[sn].cubepoints[ip[n + ix]]);
11697 mcp->grad[0] += v[0].x;
11698 mcp->grad[1] += v[0].y;
11699 mcp->grad[2] += v[0].z;
11702 for (n = 0, mcp = mc->c[sn].cubepoints; mcp->key >= 0; mcp++) {
11703 if (mcp->grad[0] != 0.0 || mcp->grad[1] != 0.0 || mcp->grad[2] != 0.0) {
11704 dd = 1.0 / sqrt(mcp->grad[0] * mcp->grad[0] + mcp->grad[1] * mcp->grad[1] + mcp->grad[2] * mcp->grad[2]);
11705 mcp->grad[0] *= dd;
11706 mcp->grad[1] *= dd;
11707 mcp->grad[2] *= dd;
11712 MoleculeCallback_notifyModification(mol, 0);
11716 char *MyAppCallback_getDocumentHomeDir(void);
11720 asprintf(&s, "%s/%s", MyAppCallback_getDocumentHomeDir(), "mcube_log.txt");
11721 fp = fopen(s, "w");
11724 for (n = 0; n < mc->nx * mc->ny * mc->nz; n++) {
11725 if (mc->dp[n] == DBL_MAX)
11727 if (dmax < mc->dp[n])
11729 if (dmin > mc->dp[n])
11736 dmax = 1.001 * dmax;
11737 fprintf(fp, "thres = %g = 100\n", mc->thres);
11738 for (iz = 0; iz < mc->nz; iz++) {
11739 fprintf(fp, "z = %d\n", iz);
11740 for (iy = 0; iy < mc->ny; iy++) {
11741 for (ix = 0; ix < mc->nx; ix++) {
11742 n = (ix * ny + iy) * nz + iz;
11745 fprintf(fp, " XXX ");
11747 dd = dd * 100 / mc->thres;
11750 else if (dd < -999.0)
11752 fprintf(fp, "%4d ", (int)(dd));
11760 for (sn = 0; sn <= 1; sn++) {
11761 for (n = 0; n < mc->c[sn].ncubepoints; n++) {
11762 MCubePoint *mcp = mc->c[sn].cubepoints + n;
11767 iz = nn / 3 % mc->nz;
11768 iy = nn / (3 * mc->nz) % mc->ny;
11769 ix = nn / (3 * mc->nz * mc->ny);
11770 fprintf(fp, "%c%d:[%d,%d,%d,%d] (%g,[%g,%g,%g],[%g,%g,%g])\n", (sn == 0 ? 'p' : 'P'),
11771 n, ix, iy, iz, iix,
11772 mcp->d, mcp->pos[0], mcp->pos[1], mcp->pos[2], mcp->grad[0], mcp->grad[1], mcp->grad[2]);
11774 for (n = 0; n < mc->c[sn].ntriangles; n += 3) {
11775 if (mc->c[sn].triangles[n] < 0)
11777 fprintf(fp, "%c%d:(%d,%d,%d)\n", (sn == 0 ? 't' : 'T'), n / 3,
11778 mc->c[sn].triangles[n], mc->c[sn].triangles[n + 1], mc->c[sn].triangles[n + 2]);