// Action_ClusterDihedral::ReadDihedrals() int Action_ClusterDihedral::ReadDihedrals(std::string const& fname) { CpptrajFile infile; char buffer[256]; int a1, a2, a3, a4, bins; double min; if ( infile.OpenRead( fname ) ) return 1; mprintf("\tReading dihedral information from %s\n", fname.c_str()); while (infile.Gets(buffer, 256)==0) { // Expected line format: At#1 At#2 At#3 At#4 Bins Min // ATOM NUMBERS SHOULD START FROM 1! int nvals = sscanf(buffer, "%i %i %i %i %i %lf", &a1, &a2, &a3, &a4, &bins, &min); if (nvals < 5) { mprinterr("Error: Dihedral file %s: Expected at least 5 values, got %i\n", fname.c_str(), nvals); mprinterr("Error: Problem line: [%s]\n",buffer); mprinterr("Error: Expected format: At#1 At#2 At#3 At#4 Bins [Min]\n"); return 1; // This should automatically close infile through destructor. } if (nvals < 6) min = minimum_; DCmasks_.push_back( DCmask(a1-1, a2-1, a3-1, a4-1, bins, min ) ); mprintf("\t\t(%i)-(%i)-(%i)-(%i) Bins=%i Min=%.3f\n",a1,a2,a3,a4,bins,min); } mprintf("\tRead %zu dihedrals.\n", DCmasks_.size()); infile.CloseFile(); return 0; }
/** Open the Charmm PSF file specified by filename and set up topology data. * Mask selection requires natom, nres, names, resnames, resnums. */ int Parm_CharmmPsf::ReadParm(FileName const& fname, Topology &parmOut) { const size_t TAGSIZE = 10; char tag[TAGSIZE]; tag[0]='\0'; CpptrajFile infile; if (infile.OpenRead(fname)) return 1; mprintf(" Reading Charmm PSF file %s as topology file.\n",infile.Filename().base()); // Read the first line, should contain PSF... const char* buffer = 0; if ( (buffer=infile.NextLine()) == 0 ) return 1; // Advance to <ntitle> !NTITLE int ntitle = FindTag(tag, "!NTITLE", 7, infile); // Only read in 1st title. Skip any asterisks. std::string psftitle; if (ntitle > 0) { buffer = infile.NextLine(); const char* ptr = buffer; while (*ptr != '\0' && (*ptr == ' ' || *ptr == '*')) ++ptr; psftitle.assign( ptr ); } parmOut.SetParmName( NoTrailingWhitespace(psftitle), infile.Filename() ); // Advance to <natom> !NATOM int natom = FindTag(tag, "!NATOM", 6, infile); if (debug_>0) mprintf("\tPSF: !NATOM tag found, natom=%i\n", natom); // If no atoms, probably issue with PSF file if (natom < 1) { mprinterr("Error: No atoms in PSF file.\n"); return 1; } // Read the next natom lines int psfresnum = 0; char psfresname[6]; char psfname[6]; char psftype[6]; double psfcharge; double psfmass; for (int atom=0; atom < natom; atom++) { if ( (buffer=infile.NextLine()) == 0 ) { mprinterr("Error: ReadParmPSF(): Reading atom %i\n",atom+1); return 1; } // Read line // ATOM# SEGID RES# RES ATNAME ATTYPE CHRG MASS (REST OF COLUMNS ARE LIKELY FOR CMAP AND CHEQ) sscanf(buffer,"%*i %*s %i %s %s %s %lf %lf",&psfresnum, psfresname, psfname, psftype, &psfcharge, &psfmass); parmOut.AddTopAtom( Atom( psfname, psfcharge, psfmass, psftype), Residue( psfresname, psfresnum, ' ', ' '), 0 ); } // END loop over atoms // Advance to <nbond> !NBOND int bondatoms[9]; int nbond = FindTag(tag, "!NBOND", 6, infile); if (nbond > 0) { if (debug_>0) mprintf("\tPSF: !NBOND tag found, nbond=%i\n", nbond); int nlines = nbond / 4; if ( (nbond % 4) != 0) nlines++; for (int bondline=0; bondline < nlines; bondline++) { if ( (buffer=infile.NextLine()) == 0 ) { mprinterr("Error: ReadParmPSF(): Reading bond line %i\n",bondline+1); return 1; } // Each line has 4 pairs of atom numbers int nbondsread = sscanf(buffer,"%i %i %i %i %i %i %i %i",bondatoms,bondatoms+1, bondatoms+2,bondatoms+3, bondatoms+4,bondatoms+5, bondatoms+6,bondatoms+7); // NOTE: Charmm atom nums start from 1 for (int bondidx=0; bondidx < nbondsread; bondidx+=2) parmOut.AddBond(bondatoms[bondidx]-1, bondatoms[bondidx+1]-1); } } else mprintf("Warning: PSF has no bonds.\n"); // Advance to <nangles> !NTHETA int nangle = FindTag(tag, "!NTHETA", 7, infile); if (nangle > 0) { if (debug_>0) mprintf("\tPSF: !NTHETA tag found, nangle=%i\n", nangle); int nlines = nangle / 3; if ( (nangle % 3) != 0) nlines++; for (int angleline=0; angleline < nlines; angleline++) { if ( (buffer=infile.NextLine()) == 0) { mprinterr("Error: Reading angle line %i\n", angleline+1); return 1; } // Each line has 3 groups of 3 atom numbers int nanglesread = sscanf(buffer,"%i %i %i %i %i %i %i %i %i",bondatoms,bondatoms+1, bondatoms+2,bondatoms+3, bondatoms+4,bondatoms+5, bondatoms+6,bondatoms+7, bondatoms+8); for (int angleidx=0; angleidx < nanglesread; angleidx += 3) parmOut.AddAngle( bondatoms[angleidx ]-1, bondatoms[angleidx+1]-1, bondatoms[angleidx+2]-1 ); } } else mprintf("Warning: PSF has no angles.\n"); // Advance to <ndihedrals> !NPHI int ndihedral = FindTag(tag, "!NPHI", 5, infile); if (ndihedral > 0) { if (debug_>0) mprintf("\tPSF: !NPHI tag found, ndihedral=%i\n", ndihedral); int nlines = ndihedral / 2; if ( (ndihedral % 2) != 0) nlines++; for (int dihline = 0; dihline < nlines; dihline++) { if ( (buffer=infile.NextLine()) == 0) { mprinterr("Error: Reading dihedral line %i\n", dihline+1); return 1; } // Each line has 2 groups of 4 atom numbers int ndihread = sscanf(buffer,"%i %i %i %i %i %i %i %i",bondatoms,bondatoms+1, bondatoms+2,bondatoms+3, bondatoms+4,bondatoms+5, bondatoms+6,bondatoms+7); for (int dihidx=0; dihidx < ndihread; dihidx += 4) parmOut.AddDihedral( bondatoms[dihidx ]-1, bondatoms[dihidx+1]-1, bondatoms[dihidx+2]-1, bondatoms[dihidx+3]-1 ); } } else mprintf("Warning: PSF has no dihedrals.\n"); mprintf("\tPSF contains %i atoms, %i residues.\n", parmOut.Natom(), parmOut.Nres()); infile.CloseFile(); return 0; }
// Action_Spam::init() Action::RetType Action_Spam::Init(ArgList& actionArgs, TopologyList* PFL, DataSetList* DSL, DataFileList* DFL, int debugIn) { // Always use imaged distances InitImaging(true); // This is needed everywhere in this function scope FileName filename; // See if we're doing pure water. If so, we don't need a peak file purewater_ = actionArgs.hasKey("purewater"); if (purewater_) { // We still need the cutoff double cut = actionArgs.getKeyDouble("cut", 12.0); cut2_ = cut * cut; doublecut_ = 2 * cut; onecut2_ = 1 / cut2_; // See if we write to a data file datafile_ = actionArgs.GetStringKey("out"); // Generate the data set name, and hold onto the master data set list std::string ds_name = actionArgs.GetStringKey("name"); if (ds_name.empty()) ds_name = myDSL_.GenerateDefaultName("SPAM"); // We only have one data set averaging over every water. Add it here myDSL_.AddSet(DataSet::DOUBLE, ds_name, NULL); solvname_ = actionArgs.GetStringKey("solv"); if (solvname_.empty()) solvname_ = std::string("WAT"); }else { // Get the file name with the peaks defined in it filename.SetFileName( actionArgs.GetStringNext() ); if (filename.empty() || !File::Exists(filename)) { mprinterr("Spam: Error: Peak file [%s] does not exist!\n", filename.full()); return Action::ERR; } // Get the remaining optional arguments solvname_ = actionArgs.GetStringKey("solv"); if (solvname_.empty()) solvname_ = std::string("WAT"); reorder_ = actionArgs.hasKey("reorder"); bulk_ = actionArgs.getKeyDouble("bulk", 0.0); double cut = actionArgs.getKeyDouble("cut", 12.0); cut2_ = cut * cut; doublecut_ = 2 * cut; onecut2_ = 1 / cut2_; std::string infoname = actionArgs.GetStringKey("info"); if (infoname.empty()) infoname = std::string("spam.info"); infofile_ = DFL->AddCpptrajFile(infoname, "SPAM info"); if (infofile_ == 0) return Action::ERR; // The default maskstr is the Oxygen atom of the solvent summaryfile_ = actionArgs.GetStringKey("summary"); // Divide site size by 2 to make it half the edge length (or radius) site_size_ = actionArgs.getKeyDouble("site_size", 2.5) / 2.0; sphere_ = actionArgs.hasKey("sphere"); // If it's a sphere, square the radius to compare with if (sphere_) site_size_ *= site_size_; datafile_ = actionArgs.GetStringKey("out"); std::string ds_name = actionArgs.GetStringKey("name"); if (ds_name.empty()) ds_name = myDSL_.GenerateDefaultName("SPAM"); // Parse through the peaks file and extract the peaks CpptrajFile peakfile; if (peakfile.OpenRead(filename)) { mprinterr("SPAM: Error: Could not open %s for reading!\n", filename.full()); return Action::ERR; } std::string line = peakfile.GetLine(); int npeaks = 0; while (!line.empty()) { if (sscanf(line.c_str(), "%d", &npeaks) != 1) { line = peakfile.GetLine(); continue; } line = peakfile.GetLine(); break; } while (!line.empty()) { double x, y, z, dens; if (sscanf(line.c_str(), "C %lg %lg %lg %lg", &x, &y, &z, &dens) != 4) { line = peakfile.GetLine(); continue; } line = peakfile.GetLine(); peaks_.push_back(Vec3(x, y, z)); } peakfile.CloseFile(); // Check that our initial number of peaks matches our parsed peaks. Warn // otherwise if (npeaks != (int)peaks_.size()) mprinterr("SPAM: Warning: %s claims to have %d peaks, but really has %d!\n", filename.full(), npeaks, peaks_.size()); // Now add all of the data sets MetaData md(ds_name); for (int i = 0; i < (int)peaks_.size(); i++) { md.SetAspect( integerToString(i+1) ); // TODO: Should this be Idx? if (myDSL_.AddSet(DataSet::DOUBLE, md) == 0) return Action::ERR; // Add a new list of integers to keep track of omitted frames std::vector<int> vec; peakFrameData_.push_back(vec); } } // Print info now if (purewater_) { mprintf("SPAM: Calculating bulk value for pure solvent\n"); if (!datafile_.empty()) mprintf("SPAM: Printing solvent energies to %s\n", datafile_.c_str()); mprintf("SPAM: Using a %.2f Angstrom non-bonded cutoff with shifted EEL.\n", sqrt(cut2_)); if (reorder_) mprintf("SPAM: Warning: Re-ordering makes no sense for pure solvent.\n"); if (!summaryfile_.empty()) mprintf("SPAM: Printing solvent SPAM summary to %s\n", summaryfile_.c_str()); }else { mprintf("SPAM: Solvent [%s] density peaks taken from %s.\n", solvname_.c_str(), filename.base()); mprintf("SPAM: %d density peaks will be analyzed from %s.\n", peaks_.size(), filename.base()); mprintf("SPAM: Occupation information printed to %s.\n", infofile_->Filename().full()); mprintf("SPAM: Sites are "); if (sphere_) mprintf("spheres with diameter %.3lf\n", site_size_); else mprintf("boxes with edge length %.3lf\n", site_size_); if (reorder_) { mprintf("SPAM: Re-ordering trajectory so each site always has "); mprintf("the same water molecule.\n"); } if (summaryfile_.empty() && datafile_.empty()) { if (!reorder_) { mprinterr("SPAM: Error: Not re-ordering trajectory or calculating energies. "); mprinterr("Nothing to do!\n"); return Action::ERR; } mprintf("SPAM: Not calculating any SPAM energies\n"); }else { mprintf("SPAM: Using a non-bonded cutoff of %.2lf Ang. with a EEL shifting function.\n", sqrt(cut2_)); mprintf("SPAM: Bulk solvent SPAM energy taken as %.3lf kcal/mol\n", bulk_); } } mprintf("#Citation: Cui, G.; Swails, J.M.; Manas, E.S.; \"SPAM: A Simple Approach\n" "# for Profiling Bound Water Molecules\"\n" "# J. Chem. Theory Comput., 2013, 9 (12), pp 5539–5549.\n"); return Action::OK; }
/** Header is 256 4-byte words. Integer unless otherwise noted. First 56 words are: * 0-2: columns, rows, sections (fastest changing to slowest) * 3: mode: 0 = envelope stored as signed bytes (from -128 lowest to 127 highest) * 1 = Image stored as Integer*2 * 2 = Image stored as Reals * 3 = Transform stored as Complex Integer*2 * 4 = Transform stored as Complex Reals * 5 == 0 * 4-6: Column, row, and section offsets * 7-9: Intervals along X, Y, Z * 10-15: float; 3x cell lengths (Ang) and 3x cell angles (deg) * 16-18: Map of which axes correspond to cols, rows, sections (1,2,3 = x,y,z) * 19-21: float; Min, max, and mean density * 22-24: Space group, bytes used for storing symm ops, flag for skew transform * If skew flag != 0, skew transformation is from standard orthogonal * coordinate frame (as used for atoms) to orthogonal map frame, as: * Xo(map) = S * (Xo(atoms) - t) * 25-33: Skew matrix 'S' (in order S11, S12, S13, S21 etc) * 34-36: Skew translation 't' * 37-51: For future use and can be skipped. * 52: char; 'MAP ' * 53: char; machine stamp for determining endianness * 54: float; RMS deviation of map from mean * 55: Number of labels */ int DataIO_CCP4::ReadData(FileName const& fname, DataSetList& datasetlist, std::string const& dsname) { CpptrajFile infile; if (infile.OpenRead( fname )) return 1; // Read first 56 words of the header into a buffer. headerbyte buffer; if (infile.Read(buffer.i, 224*sizeof(unsigned char)) < 1) { mprinterr("Error: Could not buffer CCP4 header.\n"); return 1; } if (debug_ > 0) mprintf("DEBUG: MAP= '%c %c %c %c' MACHST= '%x %x %x %x'\n", buffer.c[208], buffer.c[209], buffer.c[210], buffer.c[211], buffer.c[212], buffer.c[213], buffer.c[214], buffer.c[215]); // SANITY CHECK if (!MapCharsValid(buffer.c + 208)) { mprinterr("Error: CCP4 file missing 'MAP ' string at word 53\n"); return 1; } // Check endianess bool isBigEndian = (buffer.c[212] == 0x11 && buffer.c[213] == 0x11 && buffer.c[214] == 0x00 && buffer.c[215] == 0x00); if (!isBigEndian) { if (debug_ > 0) mprintf("DEBUG: Little endian.\n"); // SANITY CHECK if ( !(buffer.c[212] == 0x44 && buffer.c[213] == 0x41 && buffer.c[214] == 0x00 && buffer.c[215] == 0x00) ) mprintf("Warning: Invalid machine stamp: %x %x %x %x : assuming little endian.\n", buffer.c[212], buffer.c[213], buffer.c[214], buffer.c[215]); } else { if (debug_ > 0) mprintf("DEBUG: Big endian.\n"); // Perform endian swapping on header if necessary endian_swap(buffer.i, 56); } // Print DEBUG info if (debug_ > 0) { mprintf("DEBUG: Columns=%i Rows=%i Sections=%i\n", buffer.i[0], buffer.i[1], buffer.i[2]); mprintf("DEBUG: Mode=%i\n", buffer.i[3]); mprintf("DEBUG: Offsets: C=%i R=%i S=%i\n", buffer.i[4], buffer.i[5], buffer.i[6]); mprintf("DEBUG: NXYZ={ %i %i %i }\n", buffer.i[7], buffer.i[8], buffer.i[9]); mprintf("DEBUG: Box XYZ={ %f %f %f } ABG={ %f %f %f }\n", buffer.f[10], buffer.f[11], buffer.f[12], buffer.f[13], buffer.f[14], buffer.f[15]); mprintf("DEBUG: Map: ColAxis=%i RowAxis=%i SecAxis=%i\n", buffer.i[16], buffer.i[17], buffer.i[18]); mprintf("DEBUG: SpaceGroup#=%i SymmOpBytes=%i SkewFlag=%i\n", buffer.i[22], buffer.i[23], buffer.i[24]); const int* MSKEW = buffer.i + 25; mprintf("DEBUG: Skew matrix: %i %i %i\n" " %i %i %i\n" " %i %i %i\n", MSKEW[0], MSKEW[1], MSKEW[2], MSKEW[3], MSKEW[4], MSKEW[5], MSKEW[6], MSKEW[7], MSKEW[8]); const int* TSKEW = buffer.i + 34; mprintf("DEBUG: Skew translation: %i %i %i\n", TSKEW[0], TSKEW[1], TSKEW[2]); mprintf("DEBUG: Nlabels=%i\n", buffer.i[55]); } // Check input data. Only support mode 2 for now. if (buffer.i[3] != 2) { mprinterr("Error: Mode %i; currently only mode 2 for CCP4 files is supported.\n", buffer.i[3]); return 1; } // Check offsets. if (buffer.i[4] != 0 || buffer.i[5] != 0 || buffer.i[6] != 0) mprintf("Warning: Non-zero offsets present. This is not yet supported and will be ignored.\n"); // Check that mapping is col=x row=y section=z if (buffer.i[16] != 1 || buffer.i[17] != 2 || buffer.i[18] != 3) { mprinterr("Error: Currently only support cols=X, rows=Y, sections=Z\n"); return 1; } if (buffer.i[24] != 0) { mprintf("Warning: Skew information present but not yet supported and will be ignored.\n"); return 1; } // Read 10 80 character text labels char Labels[801]; Labels[800] = '\0'; infile.Read( Labels, 200*wSize ); mprintf("\t%s\n", Labels); // Symmetry records: operators separated by * and grouped into 'lines' of 80 characters int NsymmRecords = buffer.i[23] / 80; if (NsymmRecords > 0) { char symBuffer[81]; mprintf("\t%i symmetry records.\n", NsymmRecords); for (int ib = 0; ib != NsymmRecords; ib++) { infile.Gets( symBuffer, 80 ); mprintf("\t%s\n", symBuffer); } } // Add grid data set. Default to float for now. DataSet* gridDS = datasetlist.AddSet( DataSet::GRID_FLT, dsname, "GRID" ); if (gridDS == 0) return 1; DataSet_GridFlt& grid = static_cast<DataSet_GridFlt&>( *gridDS ); // Allocate grid from dims and spacing. FIXME OK to assume zero origin? if (grid.Allocate_N_O_Box( buffer.i[7], buffer.i[8], buffer.i[9], Vec3(0.0), Box(buffer.f + 10) ) != 0) { mprinterr("Error: Could not allocate grid.\n"); return 1; } // FIXME: Grids are currently indexed so Z is fastest changing. // Should be able to change indexing in grid DataSet. size_t mapSize = buffer.i[7] * buffer.i[8] * buffer.i[9]; mprintf("\tCCP4 map has %zu elements\n", mapSize); mprintf("\tDensity: Min=%f Max=%f Mean=%f RMS=%f\n", buffer.f[19], buffer.f[20], buffer.f[21], buffer.f[54]); std::vector<float> mapbuffer( mapSize ); int mapBytes = mapSize * wSize; int numRead = infile.Read( &mapbuffer[0], mapBytes ); if (numRead < 1) { mprinterr("Error: Could not read CCP4 map data.\n"); return 1; } else if (numRead < mapBytes) mprintf("Warning: Expected %i bytes, read only %i bytes\n", mapBytes, numRead); if (isBigEndian) endian_swap(&mapbuffer[0], mapSize); // FIXME: Place data into grid DataSet with correct ordering. int gidx = 0; int NXY = buffer.i[7] * buffer.i[8]; for (int ix = 0; ix != buffer.i[7]; ix++) for (int iy = 0; iy != buffer.i[8]; iy++) for (int iz = 0; iz != buffer.i[9]; iz++) { int midx = (iz * NXY) + (iy * buffer.i[7]) + ix; grid[gidx++] = mapbuffer[midx]; } infile.CloseFile(); return 0; }
/** Load Karplus parameters from input file. * Expected format: * - {type}<+|-| ><a[4]><+|-| ><b[4]><+|-| ><c[4]><+|-| ><d[4]><A[6]><B[6]><C[6]>{<D[6]>} * <reslabel[4]>* * \return 0 on success, 1 on error */ int Action_Jcoupling::loadKarplus(std::string filename) { char buffer[512],residue[5]; char *end, *ptr; int i; CpptrajFile KarplusFile; karplusConstant KC; karplusConstantList* currentResList=0; std::string CurrentRes; karplusConstantMap::iterator reslist; if (filename.empty()) { mprinterr("Error: jcoupling: Could not find Karplus parameter file.\n"); return 1; } if (KarplusFile.OpenRead( filename )) { mprinterr("Error: jcoupling: Could not read Karplus parameter file %s\n", filename.c_str()); mprinterr("Error: Ensure the file exists and is readable.\n"); return 1; } // residue is only for reading in 4 chars for residue names residue[4]='\0'; // Read through all lines of the file while (KarplusFile.Gets(buffer,512)==0) { // Skip newlines and comments if (buffer[0]=='\n' || buffer[0]=='#') continue; ptr=buffer; // First char is optional type. If optional type is C, then the Karplus // function specified in Perez et al. JACS (2001) 123 will be used, and // A, B, and C will be taken as C0, C1, and C2. if(ptr[0]=='C') { KC.type=1; ptr++; } else { KC.type=0; } // Read atom names with optional preceding character (+, -) for (i=0; i<4; i++) { if (*ptr=='+') KC.offset[i]=1; else if (*ptr=='-') KC.offset[i]=-1; else KC.offset[i]=0; ++ptr; char *endchar = ptr + 4; char savechar = *endchar; *endchar = '\0'; KC.atomName[i] = ptr; *endchar = savechar; ptr += 4; //mprintf("DEBUG:\tAtomName %i [%s]\n",i,KC.atomName[i]); } // Read parameters // NOTE: Using sscanf here instead of atof since the 4th parameter is // optional, behavior is undefined for accessing uninitialized // portion of buffer. i = sscanf(ptr, "%6lf%6lf%6lf%6lf",KC.C,KC.C+1,KC.C+2,KC.C+3); if (i<3) { mprintf("Error: jcoupling: Expected at least 3 Karplus parameters, got %i\n",i); mprintf(" Line: [%s]\n",buffer); return 1; } else if (i==3) KC.C[3]=0.0; KC.C[3]*=Constants::DEGRAD; // Place the read-in karplus constants in a map indexed by residue name // so that all karplus constants for a given residue are in one place. KarplusFile.Gets(buffer,512); // end will hold the end of the read-in buffer string end = buffer + strlen(buffer); for (ptr = buffer; ptr < end; ptr+=4) { if (*ptr=='\n') continue; residue[0] = ptr[0]; residue[1] = ptr[1]; residue[2] = ptr[2]; residue[3] = ptr[3]; CurrentRes.assign(residue); //mprintf("DEBUG:\t[%s]\n",CurrentRes.c_str()); reslist = KarplusConstants_.find(CurrentRes); if (reslist == KarplusConstants_.end() ) { // List does not exist for residue yet, create it. currentResList = new karplusConstantList; KarplusConstants_.insert( reslist, std::pair<std::string,karplusConstantList*>( CurrentRes,currentResList) ); } else // Retrieve list for residue. currentResList = (*reslist).second; currentResList->push_back(KC); ++Nconstants_; } // END loop over residues in residue line } // END Gets over input file KarplusFile.CloseFile(); // DEBUG - Print out all parameters if (debug_>0) { mprintf(" KARPLUS PARAMETERS:\n"); for (reslist=KarplusConstants_.begin(); reslist!=KarplusConstants_.end(); ++reslist) { mprintf("\t[%4s]\n",(*reslist).first.c_str()); for (karplusConstantList::iterator kc = currentResList->begin(); kc != currentResList->end(); ++kc) { mprintf("\t\t%1i",(*kc).type); mprintf(" %4s",*((*kc).atomName[0])); mprintf(" %4s",*((*kc).atomName[1])); mprintf(" %4s",*((*kc).atomName[2])); mprintf(" %4s",*((*kc).atomName[3])); mprintf(" %i %i %i %i",(*kc).offset[0],(*kc).offset[1],(*kc).offset[2],(*kc).offset[3]); mprintf(" %6.2lf %6.2lf %6.2lf %6.2lf\n",(*kc).C[0],(*kc).C[1],(*kc).C[2],(*kc).C[3]); } } } return 0; }