/* Written by Ralph C. Merkle, May 13 1991. * Xerox PARC * 3333 Coyote Hill Road * Palo Alto, CA 94304 * merkle@xerox.com * * Copyright (c) Xerox Corporation 1991. All rights reserved. * * License to copy and use this software is granted provided that * appropriate credit is given to both Xerox and the author. * * License is also granted to make and use derivative works provided * that appropriate credit is given to both Xerox and the author. * * Xerox Corporation makes no representations concerning either the * merchantability of this software or the suitability of this software * for any particular purpose. It is provided "as is" without express or * implied warranty of any kind. * * These notices must be retained in any copies of any part of this software. * * This program generates the description of a diamond block in * PolyGraf or Brookhaven format. The block can be wrapped into a * tube, and the tube can then be wrapped into a toroid (ring). * The tube is basically a sheet of diamond crystal wrapped into a * tubular shape. A few lines of additional code wraps the tube into * a toroid (ring). * * The program is invoked by saying: * tube ["brookhaven"] ["addH"] ["scale" ] "block" | "tube" | "ring" * OX OY OZ SX SY SZ TX TY TZ AX AY AZ * [ "grid" V1X V1Y V1Z V2X V2Y V2Z * [ "delete" P1X P1Y P1Z [box P2X P2Y P3Z]] | * [ "add" P1X P1Y P1Z [box P2X P2Y P3Z]] | * [ "change" "to" * [bondswanted ] P1X P1Y P1Z [box P2X P2Y P2Z]] * ]* * where all parameters are floating point numbers with the * following meanings: * "brookhaven": The output format is the Brookhaven file format. * (This format was deduced by examining a few Brookhaven * formatted files. It looks like it works, but....) * "addH": Undercoordinated atoms have hydrogens added * in semi-reasonable places. * "scale": multiply the output coordinates by the scale factor, * to increase and decrease the size. Very useful when two * structures are supposed to interlock. The inner structure can * be slightly shrunk before joint minimization is started. * "block", "tube", or "ring": specifies the general shape of * the structure being generated. * OX OY OZ -- (OX, OY, OZ) the origin of the structure. * SX SY SZ -- (SX, SY, SZ) the plane of the tube surface. * To generate a tube with a 111 surface, specify 1 1 1 * for these three parameters. * TX TY TZ -- (TX, TY, TZ) the direction tangent to the tube * surface. S and T define a plane that bisects the tube * longitudinally, dividing it into two tubular sections * of half the length. * The vectors S and T should be orthogonal. If they * are not orthogonal, an error message is generated * AX AY AZ -- (AX, AY, AZ) the axis of the tube. It must be * orthogonal to the surface vector. It defines the direction * of the axis of the tube. T and A need not be orthogonal * If they are not orthogonal, the resulting toroid will have * additional strain. * "grid" is the literal keyword "grid", and indicates that two vectors * defining the two-dimensional grid follow the keyword. * V[12][XYZ] -- define two vectors that specify the grid of * atoms for special processing. This can be used to delete * specific atoms in a plane, while not deleting other atoms in the same * plane. * "delete" is the literal keyword "delete", and indicates that a * point is to be deleted. The point [specified by three * numbers that specify the coordinates of the point] follows the * keyword. Deletion of the point is repeated at all grid spacings, * as indicated by the two grid vectors. * "add" is the literal keyword "add", and behaves similarly but in the * opposite sense as "delete" * "change" indicates that at the selected grid point * is to be altered to . can * be "any", in which case any element at the grid point is changed. * "bondswanted" specify the integer number of bonds that the modified * atom wants, e.g., carbon wants 4, nitrogen wants 3, etc. If left * unspecified, the default value of the previous atom will be used. * This produces undesirable results when changing carbon to hydrogen, * for example, where "bondswanted" should be specified as "1" * PI[XYZ] -- define the points to which the action is to be applied. * All points P + K*V1 + J*V2 will be operated on for all integer * values of K and J. If "box" is specified, then the range of * points inside the box defined by the first and second points * will be operated on. * * The output of this program is sometimes physically and chemically * plausible, and sometimes not. It is expected that the output will * at least be entered into a molecular mechanics program for energy * minimization. Whether or not the bonds in the resulting structure * are too strained to be stable depends on the circumference and thickness. * As the thickness increases and the circumference decreases, the result * will be increasing strain. The point at which this strain is too great * for the structure to remain stable is not always obvious. The program, * though, will happily generate a structure that is quite unstable. * Furthermore, surface atoms might require modification. For example, * a carbon atom might need to be replaced by a nitrogen atom at a site where * there are only three other adjacent atoms (instead of the usual four). * An alternative modification would be to add a hydrogen to the under- * coordinated carbon. Inspection of the output is required. * Note that modifying the routine "AtomName" will allow the type of * atom at each node to be modified during print out. Further, by * changing the "hydrogenation loop" that attempts to add hydrogens in * reasonable places after the main atoms have been positioned, the * types of atoms being produced can be further modified. * * A simple example of a correct command line (we assume the executable * is in the file "tube"): tube block 0 0 0 2 0 0 0 8 0 0 0 4 | | | | | | | | | Specify the axis | | | Specify the tangent | | Specify the surface | The origin. Often 0 0 0 The type of block being produced ("block", "tube", or "ring") This program can be used to generate "buckytubes" in the following (slightly indirect) fashion: First, generate a "diamond-like" tube which has a very thin 111 surface. Then edit the output file, replacing the sp3 coordinated atoms with sp2 coordinated atoms. Finally, do an energy minimization to produce the appropriate tubular graphite sheet structure. A simple example of an input that will produce an output which can be easily edited into a "buckytube" is as follows: tube tube 0.25 0.25 0.25 0.5 0.5 0.5 0 10 -10 -8 4 4 Another example (whose chemical stability is not known, but which looks pretty): tube ring 0 0 0 1 0 0 0 8 -6 0 24 22 \ grid 0 0.5 -0.5 0 1 1 delete 0.75 0.75 0.25 The following two examples are the inner and outer races of a simple molecular bearing. Note that the inner race has been reduced in size ("scale" is set to 0.9). This allows both the inner and outer race to be jointly minimized, starting from a configuration in which the inner race fits within the outer race. Specification of the inner race: tube addH scale 0.9 tube 0 0.75 0.75 1.75 0 0 0 17 -17 0 5.25 5.25 \ grid 0 0.5 -0.5 0 1 1 \ delete 1.25 0.0 0.0 \ grid 0 0.25 0 0 0 0.25 \ change O_3 to S_3 1.5 0 0 \ change N_3 to C_3 bondswanted 4 0 0 0 box 0.75 0 0 Specification of the outer race: tube addH tube 1.25 1.5 1.5 1.75 0 0 0 23 -23 0 2.75 2.75 \ grid 0 0.25 -0.25 0 1 1 \ delete 0 0.5 0.5 \ delete 0.25 0.5 0.5 \ grid 0 0.25 -0.25 0 0.25 0.25 \ change N_3 to C_3 bondswanted 4 0.75 0 0 box 4 0 0 \ grid 0.25 0 0 0 0.25 -0.25 \ delete 0 0 0 box 0 0.25 0.25 An example of a small toroid (of dubious physical stability): tube ring 0 0 0 1.50 0 0 0 5 3 0 53 -45 \ grid 0 0.5 0.5 0 1.0 -1.0 \ delete 1.25 0.75 0.75 \ grid 0 0.25 0.25 0 0.25 -0.25 \ change O_3 to S_3 1.25 0.25 0.25 \ grid 0 5 3 0 7.25 -5.25 \ delete 1.25 0 0 box 1.25 6.90625 1.78125 A larger toroid with several fiddly little changes: ------------------------------------------------- tube brookhaven addH \ tube 0 0 0 1.75 0 0 0 8 -6 0 96 94 \ grid 0 0.5 -0.5 0 1 1 delete 1.5 0.5 0.00 \ grid 0 8 -6 0 3.25 2.75 \ delete 1 0 0 box 2 9.5 -5.0 \ grid 0 8 -6 0 3.25 2.75 \ delete 1.5 8.5 1.0 \ delete 1.5 8.0 1.5 \ delete 1.5 7.5 2.0 \ delete 1.5 7.0 2.5 \ \ delete 1.5 13 0.5 \ delete 1.5 13.5 0.0 \ delete 1.5 6.0 5.5 \ delete 1.5 6.5 5.0 \ \ delete 1.5 3.0 2.5 \ delete 1.5 10.5 -3.0 \ delete 1.5 10 -2.5 \ delete 1.5 9.5 -2 \ \ delete 1.5 10.0 5.5 \ delete 1.5 10.5 5.0 \ delete 1.5 11.0 4.5 \ delete 1.5 11.5 4.0 \ grid 0 0.5 -0.5 0 0.5 0.5 \ change N_3 to C_3 bondswanted 4 0.75 0.75 0.25 \ change N_3 to C_3 bondswanted 4 1.00 0.00 0.00 \ change N_3 to C_3 bondswanted 4 1.25 0.25 0.25 \ grid 0 4 -3 0 3.25 2.75 \ change C_3 to N_3 bondswanted 3 0.75 10.75 -0.75 \ change C_3 to N_3 bondswanted 3 0.75 7.25 -3.25 \ change C_3 to N_3 bondswanted 3 0.75 9.75 8.25 \ change C_3 to N_3 bondswanted 3 0.75 6.75 5.25 ------------------------------------------------- */ #define MAXATOMS 20000 #define MAXBONDS 8 #define VALENCE 4 #define DIMENSION 3 #define X 0 #define Y 1 #define Z 2 #define SURFACE 0 #define TANGENT 1 #define AXIS 2 #define XHASHSIZE 113 #define YHASHSIZE 23 #define ZHASHSIZE 11 #define WARNINGBUCKETSIZE 100 #define TRUE 1 #define EXTERIOR1 1 #define EXTERIOR2 2 #define INTERIOR1 -1 #define FALSE 0 #define NILATOM (-1) /* lattice consant for carbon. */ #define LATTICECONSTANT 3.56683 #define TWOPI (2*3.14159265358979323846) #define EPSILON 0.001 #define ODD 1 #define EVEN 0 #define BLOCKSHAPE 0 #define TUBESHAPE 1 #define RINGSHAPE 2 #define MAXSHAPE 3 #define MAXGRIDPOINTS 50 #define DELETEACTION 0 #define CHANGEACTION 1 #define ADDACTION 2 #define MAXACTION 3 #define MAXNAMELENGTH 23 #define POLYGRAFNAMELENGTH 5 #define BROOKHAVENNAMELENGTH 4 #define POLYGRAF 0 #define BROOKHAVEN 1 #include #include #include struct gridRecord { int action; char toName[MAXNAMELENGTH]; int toBondsWanted; char fromName[MAXNAMELENGTH]; double edge[DIMENSION][DIMENSION]; double point1[DIMENSION]; double point2[DIMENSION]; }; /* The following global definition is of a struct that holds the * parameters that define the shape of the object being constructed. */ struct shapeDef { int shape; double scale; double origin[DIMENSION]; double basis [DIMENSION][DIMENSION]; int gridCount; struct gridRecord grid[MAXGRIDPOINTS]; }; /* The following global variables describe a collection of bonded atoms. * The "coord" array holds the coordinates of each atom. * The "atomType" array specifies whether the atom is "odd" or "even". * The "bond" array holds the indices of the other atoms to which * this atom is bonded. bondCount[3][2] == 7 means atom 3 is * bonded to atom 7 by the second bond coming from atom 3. The * bond numbers (0, 1, 2, 3, etc.) are semantically irrelevant. They * mean nothing. In particular, just because bondCount[3][2]==7 * does not imply bondCount[7][2]==3. It might instead be * bondCount[7][1]==3 or bondCount[7][0]==3. * The bondCount array holds the count of the number of bonds from * this atom to other atoms. bondCount[3] == 4 means atom 3 has * 4 bonds coming from it. * Note that bonds are symmetric. If i is bonded to j, then j is * bonded to i. This property is insured by the routine "BondAtoms" * "lastAtomPlusOne" gives the index of the last atom in the array plus one. * "firstAtom" indicates the first atom that has actual data. */ double coord[MAXATOMS][DIMENSION]; int atomType[MAXATOMS]; int bond[MAXATOMS][MAXBONDS]; int bondCount[MAXATOMS]; int bondsWanted[MAXATOMS]; char name[MAXATOMS][MAXNAMELENGTH]; int lastAtomPlusOne = 1; int firstAtom = 1; /* The hash table global variables */ int bucket[XHASHSIZE][YHASHSIZE][ZHASHSIZE]; int nextLink[MAXATOMS]; /* The following array gives the direction of the four neighboring * atoms from a given atom in a diamond lattice. By subtracting * instead of adding, the odd/even atom distinction can be * correctly maintained. These four directions are mapped onto * four new directions to take into account the rotation of the * crystal structure specified by the user. This modified set * of vectors is then put into the array "delta". */ double delta[VALENCE][DIMENSION] = { { 0.25, 0.25, 0.25}, {-0.25, -0.25, 0.25}, { 0.25, -0.25, -0.25}, {-0.25, 0.25, -0.25} }; char *shapeList[MAXSHAPE] = { "block", "tube", "ring" }; /* A simple error routine. Print an error message and die. */ ErrAbort(s) char *s; { fprintf(stderr,"%s\n", s); exit(0); } /* Add two vectors: a = b + c */ VectorAdd(a,b,c) double a[DIMENSION]; double b[DIMENSION]; double c[DIMENSION]; {int i; for(i=0; i= 0) { bit = i % 2; return(i-bit); } else { bit = (-i) % 2; return(i-bit); }; } /* Mod computes a correct mod function. Note that % * does not compute a correct mod function. This * mod function should be portable. */ int Mod(i,m) int i,m; { if (m <= 0) ErrAbort("Mod called with negative modulus"); if (i >= 0) return(i%m); else return( (m-((-i)%m))%m ); } /* set the bottom bit. */ int SetBottomBit(i, bit) int i,bit; { return(ClearBottomBit(i)+bit); } /* computes the length of a vector. */ double Length(a) double a[DIMENSION]; { return(sqrt(Dot(a,a))); } /* computes the distance between two points */ double Distance(a,b) double a[DIMENSION]; double b[DIMENSION]; {double temp[DIMENSION]; temp[X] = a[X]-b[X]; temp[Y] = a[Y]-b[Y]; temp[Z] = a[Z]-b[Z]; return(Length(temp)); } /* Initialize the hash table. * The hash table preserves spatial relationships. If two * points A and B are adjacent to each other in three-space, * they will be in adjacent buckets in the integerized and * folded version of three-space held in the array "bucket". * This is very convenient when looking for "nearby" coordinates. */ InitializeHashTable() {int i,j,k; for(i=0; i EPSILON) veryClose = FALSE; return(veryClose); } /* Compute an integer index value suitable for use in the hash table. * Basically, simply integerize one of the three spatial * coordinates and fold it into the proper range (given by * the parameter "size"). */ HashIndex(r,size) double r; int size; {int temp; temp = (r*2.3458565130673); temp %= size; if (temp < 0) temp += size; if (temp < 0) ErrAbort("temp < 0"); if (temp >= size) ErrAbort("temp >= size"); return(temp); } /* look in the hash bucket associated with the integer coordinates */ LookInHashBucket(i,j,k,atomCoord) int i,j,k; double atomCoord[DIMENSION]; {int linkPointer; int bucketSize; static int notWarned = TRUE; bucketSize = 0; for(linkPointer = bucket[i][j][k]; linkPointer != NILATOM; linkPointer = nextLink[linkPointer]) { /* We've found an atom in this bucket which is "too close" * to the target coordinates we're searching for. * Return the pointer to the atom and exit. */ if (TooClose(coord[linkPointer], atomCoord)) return(linkPointer); /* Diagnostics code. If a bucket gets too big, performance * goes through the floor. Warn the user. If this is a problem, * increase the size of the array "bucket" and re-compile. */ if (notWarned) if (bucketSize++ > WARNINGBUCKETSIZE) { fprintf(stderr,"Over %d entries in one bucket\n",WARNINGBUCKETSIZE); notWarned = FALSE; }; }; return(NILATOM); } /* add an atom to the hash table */ AddToHashTable(atomNumber) int atomNumber; {int i,j,k; i=HashIndex(coord[atomNumber][X],XHASHSIZE); j=HashIndex(coord[atomNumber][Y],YHASHSIZE); k=HashIndex(coord[atomNumber][Z],ZHASHSIZE); if (nextLink[atomNumber] != NILATOM) { fprintf(stderr,"Attempt to add atom %d to hash table twice\n", atomNumber); ErrAbort(""); }; nextLink[atomNumber] = bucket[i][j][k]; bucket[i][j][k] = atomNumber; } /* Given the index of an atom in the data structure, add that atom * to the collection of atoms in the global data structure. */ AddAtom(atomCoord,localAtomType) double atomCoord[DIMENSION]; int localAtomType; {int i; if(lastAtomPlusOne >= MAXATOMS) ErrAbort("Too many atoms"); for(i=0;i= lastAtomPlusOne) ErrAbort("Atom1 number out of range"); if(atom2 >= lastAtomPlusOne) ErrAbort("Atom2 number out of range"); if(atom1 < firstAtom) ErrAbort("Atom1 number less than firstAtom"); if(atom2 < firstAtom) ErrAbort("Atom2 number less than firstAtom"); /* If we want to bond two atoms, then one has to be "odd" and * the other "even." If this isn't true, something's wrong. */ atomTypeOK = FALSE; if((atomType[atom1] == EVEN) && (atomType[atom2]==ODD)) atomTypeOK = TRUE; if((atomType[atom2] == EVEN) && (atomType[atom1]==ODD)) atomTypeOK = TRUE; if(atomTypeOK != TRUE) { printf(" types 1 and 2: %d %d\n",atomType[atom1], atomType[atom2]); printf(" atoms 1 and 2: %d %d\n", atom1, atom2); ErrAbort("Atom types being bonded are wrong"); }; /* See if we've already got pointers from atom1 to atom2 * and vice versa. */ hit1 = FALSE; hit2 = FALSE; for(i=0;i MAXBONDS) ErrAbort("Bond overflow, logic error"); if (bondCount[atom1] > VALENCE) fprintf(stderr,"Atom %d has more than %d bonds\n", atom1, VALENCE); }; if(hit2 == FALSE) { /* There's no bond from atom2 to atom2. Add one. */ bond[atom2][bondCount[atom2]++] = atom1; if (bondCount[atom2] > MAXBONDS) ErrAbort("Bond overflow, logic error"); if (bondCount[atom2] > VALENCE) fprintf(stderr,"Atom %d has more than %d bonds\n", atom2, VALENCE); }; } /* Get a parameter from the command line */ double GetParam(argc, argv, argNum, s) int argc; char *argv[]; int argNum; char *s; {int status; double temp; int i; if(argc <= argNum) { fprintf(stderr," argc = %d\n argNum = %d\n",argc,argNum); ErrAbort("Invalid parameter number in GetParam"); }; /* See if we're looking for "block", "tube", or "shape" */ if (strcmp(s,"shape") == 0) { for (i=0;i \"to\" \n"); fprintf(stderr," [bondswanted ] P1X P1Y P1Z [box P2X P2Y P2Z]]\n"); fprintf(stderr," ]*\n"); fprintf(stderr,"A vector O describing the origin : 0 0 0\n"); fprintf(stderr,"A vector S describing the surface: 1 1 1\n"); fprintf(stderr,"A vector T describing the tangent: 0 7 -7\n"); fprintf(stderr,"A vector A describing the axis : -4 2 2\n"); fprintf(stderr,"The surface and tangent should be orthogonal.\n"); fprintf(stderr,"Example:\n"); fprintf(stderr,"makeTube tube 0 0 0 1 1 1 0 17 -17 -4 2 2\n"); fprintf(stderr,"See the program for further examples\n"); }; argi = 1; outputFormat = POLYGRAF; if ( strcmp(argv[argi],"brook") == 0 || strcmp(argv[argi],"brookhaven") == 0 || strcmp(argv[argi],"bkv") == 0 ) { outputFormat = BROOKHAVEN; argi++; }; addH = FALSE; if ( strcmp(argv[argi],"addH") == 0 || strcmp(argv[argi],"addHydrogen") == 0 ) { addH = TRUE; argi++; }; s.scale = 1.0; if ( strcmp(argv[argi],"scale") == 0 ) { argi++; s.scale = GetParam(argc, argv, argi++, "scale"); }; s.shape = GetParam(argc, argv, argi++, "shape"); s.origin[X] = GetParam(argc, argv, argi++, "origin[X]"); s.origin[Y] = GetParam(argc, argv, argi++, "origin[Y]"); s.origin[Z] = GetParam(argc, argv, argi++, "origin[Z]"); fprintf(stderr,"\n"); s.basis[SURFACE][X] = GetParam(argc, argv, argi++, "surface[X]"); s.basis[SURFACE][Y] = GetParam(argc, argv, argi++, "surface[Y]"); s.basis[SURFACE][Z] = GetParam(argc, argv, argi++, "surface[Z]"); fprintf(stderr,"\n"); s.basis[TANGENT][X] = GetParam(argc, argv, argi++, "tangent[X]"); s.basis[TANGENT][Y] = GetParam(argc, argv, argi++, "tangent[Y]"); s.basis[TANGENT][Z] = GetParam(argc, argv, argi++, "tangent[Z]"); fprintf(stderr,"\n"); s.basis[AXIS][X] = GetParam(argc, argv, argi++, "axis[X]"); s.basis[AXIS][Y] = GetParam(argc, argv, argi++, "axis[Y]"); s.basis[AXIS][Z] = GetParam(argc, argv, argi++, "axis[Z]"); fprintf(stderr,"\n"); /* Compute "cross" just for the interest of the user. Don't actually * use it in internal computations. */ for (i=0;i= MAXGRIDPOINTS) ErrAbort("Too many grid points, increase MAXGRIDPOINTS and recompile\n"); }; }; if (argi != argc) { fprintf(stderr," Only %d command line parameters were read.\n",argi-1); ErrAbort("Change parameters and try again"); }; if(Dot(s.basis[TANGENT],s.basis[SURFACE]) != 0.0) { fprintf(stderr,"The tangent and surface vectors are not orthogonal\n"); fprintf(stderr,"The dot product is: %15f\n", Dot(s.basis[TANGENT], s.basis[SURFACE])); ErrAbort("Change vectors and try again."); }; if( Dot(s.basis[AXIS],s.basis[SURFACE]) != 0.0 ) { CrossProduct(tempCoord, s.basis[SURFACE], s.basis[TANGENT]); fprintf(stderr, "The axis is not orthogonal to the surface vector\n"); ErrAbort("Change vectors and try again."); }; /* Yes, this is a hack. Print out the internal coordinates of each atom. * This is useful in specifying the coordinates of atoms that are to be * deleted or changed. A graphical user interface would obviously be\ * much superior. In the meantime, from the atom number it's possible * to determine the atom coordinates, which can be used to modify * the atom. */ streamOut = fopen("coords","w"); /* print some headers to the output file */ if (outputFormat == POLYGRAF) { printf("BIOGRF 160\n"); printf("DESCRP %s\n", shapeList[s.shape]); } else if (outputFormat == BROOKHAVEN) { printf("HEADER Brookhaven BGRF \n"); printf("REMARK %s\n", shapeList[s.shape]); } else ErrAbort("variable \"outputFormat\" has illegal value"); printf("REMARK Version 1.02 of tube generation program\n"); printf("REMARK SX= %15f SY= %15f SZ= %15f\n", s.basis[SURFACE][X], s.basis[SURFACE][Y], s.basis[SURFACE][Z]); printf("REMARK TX= %15f TY= %15f TZ= %15f\n", s.basis[TANGENT][X], s.basis[TANGENT][Y], s.basis[TANGENT][Z]); printf("REMARK AX= %15f AY= %15f AZ= %15f\n", s.basis[AXIS][X], s.basis[AXIS][Y], s.basis[AXIS][Z]); printf("REMARK surface*tangent = %15f\n", Dot(s.basis[SURFACE],s.basis[TANGENT])); printf("REMARK surface*axis = %15f\n", Dot(s.basis[SURFACE],s.basis[AXIS])); printf("REMARK axis*tangent = %15f\n", Dot(s.basis[AXIS],s.basis[TANGENT])); /* processNext is the index of the atom to be processed next. * Notice that firstAtom <= processNext <= lastAtomPlusOne. * If processNext = lastAtomPlusOne, there are no more atoms * to process. * To start, processNext and lastAtomPlusOne are all set * equal to first atom, because there are no atoms in the * list and no atoms to process. */ firstAtom = 1; processNext = firstAtom; lastAtomPlusOne = firstAtom; /* Initialize the hash table */ InitializeHashTable(); /* Add the seed coordinate */ seed[X] = s.origin[X]; seed[Y] = s.origin[Y]; seed[Z] = s.origin[Z]; seedNum = AddAtom(seed, EVEN); seedType = atomType[seedNum]; printf("REMARK seed = %15f, %15f, %15f\n",seed[X], seed[Y], seed[Z]); printf("REMARK seedType = %d\n", seedType); if (TestCoordinates(seed, s)>0) ErrAbort("Could not create seed atom within the region."); /* Loop until the atoms processed equals the atoms in the array */ while (processNext < lastAtomPlusOne) { for(currentBond=0; currentBond0) ) printf("\nCONECT%5d", j); printf("%5d", bond[j][k]); }; printf("\n"); } else ErrAbort("Illegal outputFormat value"); }; printf("%s\n","END"); return(0); } /* * Project p1, p2, p3, and p4 along the vector v. If * the resulting values are "linear", e.g., fall in * sequential order or reverse sequential order, * return TRUE. * If the values can be MADE to fall in sequential order * by adding or subtracting an integer multiple of the * projection of p4 against 4, then also return TRUE. * If this doesn't work return FALSE. */ ModInOrder(v,p1,p2,p3,p4) double v[DIMENSION]; double p1[DIMENSION]; double p2[DIMENSION]; double p3[DIMENSION]; double p4[DIMENSION]; { double a1,a2,a3,a4; int n; /* project all four points onto the vector v */ a1 = Dot(v,p1); a2 = Dot(v,p2); a3 = Dot(v,p3); a4 = Dot(v,p4); /* make sure the "modulus" value is positive, a negative * value would foul things up later */ if (a4 < 0.0) a4 = -a4; /* Compute the magic integer "n" */ n = 0; if (a4 != 0.0) { if (a1 <= a3) n = floor((a2-a1)/a4); else n = floor((a2-a3)/a4); }; /* put a2 between a1 and a3, if possible, by adding a multiple of a4. */ a2 -= n*a4; if (InOrder(a1,a2,a3)) return(TRUE); /* A little paranoid checking, just in case...*/ a2 -= a4; if (InOrder(a1,a2,a3)) ErrAbort("Logic Error, n off by one"); a2 += 2*a4; if (InOrder(a1,a2,a3)) ErrAbort("Logic Error, n off by one"); return(FALSE); } /* * Check to see if the point p lies within the "bounding box" * defined by grid.point1 and grid.point2. The box edges are * defined by edge[1], edge[2], and edge[1] X edge[2]. ("X" is the cross * product). We also check to see if p lies within boxes * that are periodically offset from the "base box" by * increments of the edge vectors. Of course, edge[0] was * a fake, generated by taking the cross product of edge[1] * and edge[2], and we don't really want periodicity in * that direction at all. This leads to some special-case * handling of edge[0] to prevent the undesired periodicity * from occuring along that axis. */ InBox(p, grid) double p[DIMENSION]; struct gridRecord grid; { double cross[DIMENSION][DIMENSION]; double temp [DIMENSION][DIMENSION]; static double zeroVector[DIMENSION] = {0.0, 0.0, 0.0}; int i; for (i=0;i= VALENCE) ErrAbort("Logic Error bondNumber >= VALENCE"); for (dim=0; dim= Dot(s.basis[SURFACE], s.basis[SURFACE])) return(EXTERIOR1); if (s.shape == RINGSHAPE) return(INTERIOR1); if (Dot(atomCoord,s.basis[AXIS]) < 0) return(EXTERIOR1); if (Dot(atomCoord,s.basis[AXIS]) >= Dot(s.basis[AXIS],s.basis[AXIS])) return(EXTERIOR1); if (s.shape == TUBESHAPE) return(INTERIOR1); if (Dot(atomCoord,s.basis[TANGENT]) < 0) return(EXTERIOR1); if (Dot(atomCoord,s.basis[TANGENT]) >= Dot(s.basis[TANGENT],s.basis[TANGENT])) return(EXTERIOR1); if (s.shape != BLOCKSHAPE) ErrAbort("Bad shape in TestCoordinates"); return(INTERIOR1); } /* a is set to the cross product of b and c. * By definition, this means that a is orthogonal to * both b and c, and that * Length(a) = Length(b) * Length(c) * sin(theta) * where theta is the angle from b to c. */ CrossProduct(a,b,c) double a[DIMENSION], b[DIMENSION], c[DIMENSION]; { /* use a temporary just in case a and b or c overlap in memory */ double t[DIMENSION]; /* Cross products only work in three dimensions anyway, * so this routine is locked into DIMENSION = 3. */ t[X] = b[Y]*c[Z]-c[Y]*b[Z]; t[Y] = c[X]*b[Z]-b[X]*c[Z]; t[Z] = b[X]*c[Y]-c[X]*b[Y]; a[X] = t[X]; a[Y] = t[Y]; a[Z] = t[Z]; } /* This routine maps atoms around into a tube. * That is, the mapping of a rectangular object * into a tube involves connecting two opposing * faces of the object. This routine performs * that connection by mapping atoms that "fall * off" the rectangle along the X axis back into * the region occupied by the rectangle, but * starting over again near X = 0. This routine * is essential for getting the bonds around the tube * edge correct. */ MapCoordinates(atomCoord, s) double atomCoord[DIMENSION]; struct shapeDef s; { double orthoTangent[DIMENSION]; double orthoAxis[DIMENSION]; double tempVector[DIMENSION]; double dot1, dot2; double k; if (s.shape < 0) ErrAbort("Bad shape parameter: negative."); if (s.shape >= MAXSHAPE) ErrAbort("Bad shape parameter: too large."); if (s.shape == BLOCKSHAPE) return; /* turn it into a toroid */ if (s.shape == RINGSHAPE) { CrossProduct(orthoTangent, s.basis[SURFACE], s.basis[TANGENT]); dot1 = Dot(atomCoord,orthoTangent); dot2 = Dot(s.basis[AXIS], orthoTangent); k = floor(dot1/dot2); VectorCopy(tempVector, s.basis[AXIS]); ScalarMul (tempVector, k); VectorSub (atomCoord,atomCoord,tempVector); }; if (s.shape == RINGSHAPE || s.shape == TUBESHAPE) { CrossProduct(orthoAxis, s.basis[AXIS],s.basis[SURFACE]); dot1 = Dot(atomCoord,orthoAxis); dot2 = Dot(s.basis[TANGENT], orthoAxis); k = floor(dot1/dot2); VectorCopy(tempVector, s.basis[TANGENT]); ScalarMul (tempVector, k); VectorSub (atomCoord,atomCoord,tempVector); }; } /* This routine bends the nice neat crystal * coordinates that describe a nice neat diamond * crystal in three space into the bent or strained * structure of a tube or toroid. */ Bend(atomCoordOut, atomCoordIn, s) double atomCoordOut[DIMENSION]; double atomCoordIn [DIMENSION]; struct shapeDef s; { double radius1, radius2, angle1, angle2; double orthoTangent[DIMENSION]; double orthoAxis[DIMENSION]; int i; VectorCopy(atomCoordOut,atomCoordIn); if (s.shape == BLOCKSHAPE) goto adjustLatticeConstant; CrossProduct(orthoTangent, s.basis[TANGENT], s.basis[SURFACE]); CrossProduct(orthoAxis, s.basis[AXIS], s.basis[SURFACE]); angle1 = (Dot(atomCoordIn,orthoAxis)/Dot(s.basis[TANGENT],orthoAxis))*TWOPI; radius1 = Length(s.basis[TANGENT])/TWOPI + Dot(atomCoordIn,s.basis[SURFACE])/Length(s.basis[SURFACE]); atomCoordOut[X] = sin(angle1)*radius1; atomCoordOut[Y] = Dot(atomCoordIn,orthoTangent)/Dot(s.basis[AXIS],orthoTangent) * Length(s.basis[AXIS]); atomCoordOut[Z] = cos(angle1)*radius1; if(s.shape == TUBESHAPE) goto adjustLatticeConstant; /* We've finished bending it into a tube. Now bend it into a toroid. */ angle2 = (Dot(atomCoordIn,orthoTangent)/ Dot(s.basis[AXIS],orthoTangent))*TWOPI; radius2 = Length(s.basis[AXIS])/TWOPI + cos(angle1)*radius1; atomCoordOut[X] = sin(angle2)*radius2; atomCoordOut[Y] = sin(angle1)*radius1; atomCoordOut[Z] = cos(angle2)*radius2; if (s.shape != RINGSHAPE) ErrAbort("Bad shape parameter in Bend"); adjustLatticeConstant: for(i=0;i