ColumnDataWriter.cpp

00001 /*
00002 
00003 Copyright (C) University of Oxford, 2005-2011
00004 
00005 University of Oxford means the Chancellor, Masters and Scholars of the
00006 University of Oxford, having an administrative office at Wellington
00007 Square, Oxford OX1 2JD, UK.
00008 
00009 This file is part of Chaste.
00010 
00011 Chaste is free software: you can redistribute it and/or modify it
00012 under the terms of the GNU Lesser General Public License as published
00013 by the Free Software Foundation, either version 2.1 of the License, or
00014 (at your option) any later version.
00015 
00016 Chaste is distributed in the hope that it will be useful, but WITHOUT
00017 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00018 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
00019 License for more details. The offer of Chaste under the terms of the
00020 License is subject to the License being interpreted in accordance with
00021 English Law and subject to any action against the University of Oxford
00022 being under the jurisdiction of the English Courts.
00023 
00024 You should have received a copy of the GNU Lesser General Public License
00025 along with Chaste. If not, see <http://www.gnu.org/licenses/>.
00026 
00027 */
00028 
00034 #include "ColumnDataWriter.hpp"
00035 #include "ColumnDataConstants.hpp"
00036 #include "Exception.hpp"
00037 #include "Version.hpp"
00038 
00039 #include <ctype.h>
00040 #include <sstream>
00041 #include <iomanip>
00042 #include <fstream>
00043 
00044 //#include <sys/stat.h> // For chmod()
00045 
00046 ColumnDataWriter::ColumnDataWriter(const std::string& rDirectory,
00047                                    const std::string& rBaseName,
00048                                    bool cleanDirectory,
00049                                    unsigned precision)
00050     : mOutputFileHandler(rDirectory, cleanDirectory),
00051       mDirectory(rDirectory),
00052       mBaseName(rBaseName),
00053       mIsInDefineMode(true),
00054       mIsFixedDimensionSet(false),
00055       mIsUnlimitedDimensionSet(false),
00056       mUnlimitedDimensionPosition(0),
00057       mFixedDimensionSize(-1),
00058       mpCurrentOutputFile(NULL),
00059       mpCurrentAncillaryFile(NULL),
00060       mpUnlimitedDimensionVariable(NULL),
00061       mpFixedDimensionVariable(NULL),
00062       mFieldWidth(precision+7), // Allow for numbers like -1.111e-321 (where precision=3)
00063       mPrecision(precision),
00064       mHasPutVariable(false),
00065       mNeedAdvanceAlongUnlimitedDimension(false),
00066       mCommentForInfoFile("")
00067 {
00068     if (mPrecision<2 || mPrecision>20)
00069     {
00070         EXCEPTION("Precision must be between 2 and 20 (inclusive)");
00071     }
00072 }
00073 
00074 ColumnDataWriter::~ColumnDataWriter()
00075 {
00076     // Close any open output files
00077     Close();
00078 
00079     // Delete memory allocated for variables
00080     if (mpUnlimitedDimensionVariable != NULL)
00081     {
00082         delete mpUnlimitedDimensionVariable;
00083     }
00084     if (mpFixedDimensionVariable != NULL)
00085     {
00086         delete mpFixedDimensionVariable;
00087     }
00088 }
00089 
00090 std::string ColumnDataWriter::GetOutputDirectory()
00091 {
00092     return mOutputFileHandler.GetOutputDirectoryFullPath();
00093 }
00094 
00095 void ColumnDataWriter::Close()
00096 {
00097     if (mpCurrentOutputFile.get() != NULL)
00098     {
00099         mpCurrentOutputFile->close();
00100         mpCurrentOutputFile = out_stream(NULL);
00101     }
00102 
00103     if (mpCurrentAncillaryFile.get() != NULL)
00104     {
00105         mpCurrentAncillaryFile->close();
00106         mpCurrentAncillaryFile = out_stream(NULL);
00107     }
00108 }
00109 
00110 void ColumnDataWriter::CheckVariableName(const std::string& rName)
00111 {
00112     if (rName.length() == 0)
00113     {
00114         EXCEPTION("Variable name not allowed: may not be blank.");
00115     }
00116     CheckUnitsName(rName);
00117 }
00118 
00119 void ColumnDataWriter::CheckUnitsName(const std::string& rName)
00120 {
00121     for (unsigned i=0; i<rName.length(); i++)
00122     {
00123         if (!isalnum(rName[i]) && !(rName[i]=='_'))
00124         {
00125             std::string error = "Variable name/units '" + rName + "' not allowed: may only contain alphanumeric characters or '_'.";
00126             EXCEPTION(error);
00127         }
00128     }
00129 }
00130 
00131 int ColumnDataWriter::DefineUnlimitedDimension(const std::string& rDimensionName,
00132                                                const std::string& rDimensionUnits)
00133 {
00134     if (mIsUnlimitedDimensionSet)
00135     {
00136         EXCEPTION("Unlimited dimension already set. Cannot be defined twice");
00137     }
00138 
00139     if (!mIsInDefineMode)
00140     {
00141         EXCEPTION("Cannot define variables when not in Define mode");
00142     }
00143 
00144     CheckVariableName(rDimensionName);
00145     CheckUnitsName(rDimensionUnits);
00146 
00147     mUnlimitedDimensionName = rDimensionName;
00148     mUnlimitedDimensionUnits = rDimensionUnits;
00149 
00150     mpUnlimitedDimensionVariable = new DataWriterVariable;
00151     mpUnlimitedDimensionVariable->mVariableName = rDimensionName;
00152     mpUnlimitedDimensionVariable->mVariableUnits = rDimensionUnits;
00153 
00154     mIsUnlimitedDimensionSet = true;
00155 
00156     return UNLIMITED_DIMENSION_VAR_ID;
00157 }
00158 
00159 int ColumnDataWriter::DefineFixedDimension(const std::string& rDimensionName,
00160                                            const std::string& rDimensionUnits,
00161                                            long dimensionSize)
00162 {
00163     if (!mIsInDefineMode)
00164     {
00165         EXCEPTION("Cannot define variables when not in Define mode");
00166     }
00167     if (dimensionSize < 1)
00168     {
00169         EXCEPTION("Fixed dimension must be at least 1 long");
00170     }
00171 
00172     CheckVariableName(rDimensionName);
00173     CheckUnitsName(rDimensionUnits);
00174 
00175     mFixedDimensionName = rDimensionName;
00176     mFixedDimensionUnits = rDimensionUnits;
00177     mFixedDimensionSize = dimensionSize;
00178 
00179     mIsFixedDimensionSet = true;
00180 
00181     mpFixedDimensionVariable = new DataWriterVariable;
00182     mpFixedDimensionVariable->mVariableName = rDimensionName;
00183     mpFixedDimensionVariable->mVariableUnits = rDimensionUnits;
00184     return FIXED_DIMENSION_VAR_ID;
00185 }
00186 
00187 int ColumnDataWriter::DefineVariable(const std::string& rVariableName,
00188                                      const std::string& rVariableUnits)
00189 {
00190     if (!mIsInDefineMode)
00191     {
00192         EXCEPTION("Cannot define variables when not in Define mode");
00193     }
00194 
00195     CheckVariableName(rVariableName);
00196     CheckUnitsName(rVariableUnits);
00197 
00198     int variable_id;
00199 
00200     if (rVariableName == mUnlimitedDimensionName)
00201     {
00202         EXCEPTION("Variable name: " + rVariableName + " already in use as unlimited dimension");
00203     }
00204     else if (rVariableName == mFixedDimensionName)
00205     {
00206         EXCEPTION("Variable name: " + rVariableName + " already in use as fixed dimension");
00207     }
00208     else // ordinary variable
00209     {
00210         // Add the variable to the variable vector
00211         DataWriterVariable new_variable;
00212         new_variable.mVariableName = rVariableName;
00213         new_variable.mVariableUnits = rVariableUnits;
00214         mVariables.push_back(new_variable);
00215 
00216         // Use the index of the variable vector as the variable ID.
00217         // This is ok since there is no way to remove variables.
00218         variable_id = mVariables.size()-1;
00219     }
00220 
00221     return variable_id;
00222 }
00223 
00224 void ColumnDataWriter::EndDefineMode()
00225 {
00226     // Check that a dimension has been defined
00227     if (mIsFixedDimensionSet == false && mIsUnlimitedDimensionSet == false)
00228     {
00229         EXCEPTION("Cannot end define mode. No dimensions have been defined.");
00230     }
00231     // Check that at least one variable has been defined
00232     if (mVariables.size() < 1)
00233     {
00234         EXCEPTION("Cannot end define mode. No variables have been defined.");
00235     }
00236     // Calculate the width of each row
00237     int unlimited_dimension_variable = (mpUnlimitedDimensionVariable != NULL);
00238     int fixed_dimension_variable = (mpFixedDimensionVariable != NULL);
00239     if (mIsUnlimitedDimensionSet)
00240     {
00241         if (mIsFixedDimensionSet)
00242         {
00243             mRowWidth = (mVariables.size() + fixed_dimension_variable) * (mFieldWidth + SPACING);
00244             mAncillaryRowWidth = mFieldWidth + SPACING;
00245 
00246             // Write out the headers for the first position along the unlimited dimension
00247             std::stringstream suffix;
00248             suffix << std::setfill('0') << std::setw(FILE_SUFFIX_WIDTH) << mUnlimitedDimensionPosition;
00249 
00250             if (mpUnlimitedDimensionVariable != NULL)
00251             {
00252                 std::string ancillary_filename = mBaseName + "_unlimited.dat";
00253                 mpCurrentAncillaryFile = mOutputFileHandler.OpenOutputFile(ancillary_filename, std::ios::out);
00254                 (*mpCurrentAncillaryFile) << std::setiosflags(std::ios::scientific);
00255                 (*mpCurrentAncillaryFile) << std::setprecision(mPrecision);
00256                 if (mpUnlimitedDimensionVariable != NULL)
00257                 {
00258                     (*mpCurrentAncillaryFile) << mpUnlimitedDimensionVariable->mVariableName
00259                                               << "(" << mpUnlimitedDimensionVariable->mVariableUnits << ") ";
00260                 }
00261             }
00262             mAncillaryRowStartPosition = mpCurrentAncillaryFile->tellp();
00263             std::string filename = mBaseName + "_" + suffix.str() + ".dat";
00264             this->CreateFixedDimensionFile(filename);
00265         }
00266         else
00267         {
00268             mRowWidth = (mVariables.size() + unlimited_dimension_variable) * (mFieldWidth + SPACING);
00269 
00270             // Write out the column headers
00271             std::string filename = mBaseName + ".dat";
00272             mpCurrentOutputFile = mOutputFileHandler.OpenOutputFile(filename, std::ios::out);
00273             (*mpCurrentOutputFile) << std::setiosflags(std::ios::scientific);
00274             (*mpCurrentOutputFile) << std::setprecision(mPrecision);
00275             if (mpUnlimitedDimensionVariable != NULL)
00276             {
00277                 (*mpCurrentOutputFile) << mpUnlimitedDimensionVariable->mVariableName
00278                                        << "(" << mpUnlimitedDimensionVariable->mVariableUnits << ") ";
00279             }
00280             /*
00281              * Write out header(which may contain several variables) for output file.
00282              * In this scope the method "CreateFixedDimensionFile" has not been invoked,
00283              * because there is no mFixedDimensionSize available.
00284              */
00285             for (unsigned i=0; i<mVariables.size(); i++)
00286             {
00287                 (*mpCurrentOutputFile) << mVariables[i].mVariableName << "(" << mVariables[i].mVariableUnits << ")";
00288                 if (i < mVariables.size()-1)
00289                 {
00290                     (*mpCurrentOutputFile) << " ";
00291                 }
00292             }
00293             (*mpCurrentOutputFile) << std::endl;
00294             mRowStartPosition = mpCurrentOutputFile->tellp();
00295 
00296             // Write out a line of blank space which is #variables * (mFieldWidth + 1) -1
00297             std::string blank_line(mRowWidth, ' ');
00298             (*mpCurrentOutputFile) << blank_line;
00299         }
00300     }
00301     else
00302     {
00303         // The fixed dimension must be set at this point or we wouldn't be here
00304         mRowWidth = (mVariables.size() + fixed_dimension_variable) * (mFieldWidth + SPACING);
00305         std::string filename = mBaseName + ".dat";
00306         this->CreateFixedDimensionFile(filename);
00307     }
00308 
00309     // Write info file
00310     std::string infoname = mBaseName + ".info";
00311     this->CreateInfoFile(infoname);
00312 
00313     mIsInDefineMode = false;
00314 }
00315 
00316 void ColumnDataWriter::CreateFixedDimensionFile(const std::string& rFileName)
00317 {
00318     // Create new data file
00319     mpCurrentOutputFile = mOutputFileHandler.OpenOutputFile(rFileName, std::ios::out);
00320     (*mpCurrentOutputFile) << std::setiosflags(std::ios::scientific);
00321     (*mpCurrentOutputFile) << std::setprecision(mPrecision);
00322     if (mpFixedDimensionVariable != NULL)
00323     {
00324         (*mpCurrentOutputFile) << mpFixedDimensionVariable->mVariableName
00325                                << "(" << mpFixedDimensionVariable->mVariableUnits << ") ";
00326     }
00327     // Write out the column headers and spaces for the rest of the file
00328     for (unsigned i = 0; i < mVariables.size(); i++)
00329     {
00330         (*mpCurrentOutputFile) << mVariables[i].mVariableName << "(" << mVariables[i].mVariableUnits << ")";
00331         if (i < mVariables.size()-1)
00332         {
00333             (*mpCurrentOutputFile) << " ";
00334         }
00335     }
00336     (*mpCurrentOutputFile) << std::endl;
00337     mRowStartPosition = mpCurrentOutputFile->tellp();
00338     std::string blank_line(mRowWidth, ' ');
00339     for (int i = 0; i < mFixedDimensionSize; i++)
00340     {
00341         (*mpCurrentOutputFile) << blank_line << std::endl;
00342     }
00343 }
00344 
00345 void ColumnDataWriter::CreateInfoFile(const std::string& rFileName)
00346 {
00347     // Create new info file
00348     out_stream p_info_file = mOutputFileHandler.OpenOutputFile(rFileName, std::ios::out);
00349     (*p_info_file) << "FIXED " << mFixedDimensionSize << std::endl;
00350     (*p_info_file) << "UNLIMITED " << mIsUnlimitedDimensionSet << std::endl;
00351     (*p_info_file) << "VARIABLES " << mVariables.size() << std::endl;
00352     if (mCommentForInfoFile != "")
00353     {
00354         *p_info_file << mCommentForInfoFile << std::endl;
00355     }
00356     *p_info_file << ChasteBuildInfo::GetProvenanceString();
00357     p_info_file->close();
00358 }
00359 
00360 void ColumnDataWriter::DoAdvanceAlongUnlimitedDimension()
00361 {
00362     mHasPutVariable = false;
00363     mNeedAdvanceAlongUnlimitedDimension = false;
00364 
00365     if (mIsUnlimitedDimensionSet)
00366     {
00367         if (mIsFixedDimensionSet)
00368         {
00369             //first close the current file before creating another one
00370             mpCurrentOutputFile->close();
00371             std::stringstream suffix;
00372             suffix << std::setfill('0') << std::setw(FILE_SUFFIX_WIDTH) << mUnlimitedDimensionPosition + 1;
00373 
00374             std::string filename = mBaseName + "_" + suffix.str() + ".dat";
00375             this->CreateFixedDimensionFile(filename);
00376         }
00377         else
00378         {
00379             //go to the end of the current line
00380             mpCurrentOutputFile->seekp(mRowStartPosition+mRowWidth);
00381             (*mpCurrentOutputFile) << std::endl;
00382             mRowStartPosition = mpCurrentOutputFile->tellp();
00383             std::string blank_line(mRowWidth,' ');
00384             (*mpCurrentOutputFile) << blank_line;
00385         }
00386     }
00387     else
00388     {
00389         EXCEPTION("Cannot advance along unlimited dimension if it is not defined");
00390     }
00391     mUnlimitedDimensionPosition++;
00392 }
00393 
00394 void ColumnDataWriter::AdvanceAlongUnlimitedDimension()
00395 {
00396     if (mHasPutVariable)
00397     {
00398         mNeedAdvanceAlongUnlimitedDimension = true;
00399     }
00400 }
00401 
00402 void ColumnDataWriter::PutVariable(int variableID, double variableValue, long dimensionPosition)
00403 {
00404     if (mNeedAdvanceAlongUnlimitedDimension)
00405     {
00406         DoAdvanceAlongUnlimitedDimension();
00407     }
00408 
00409     // Check that we are not in define mode
00410     if (mIsInDefineMode)
00411     {
00412         EXCEPTION("Cannot put variables when in Define mode");
00413     }
00414     // Check that variableID is in range (exception)
00415     if (variableID > (int)mVariables.size() ||
00416         (variableID != UNLIMITED_DIMENSION_VAR_ID &&
00417          variableID != FIXED_DIMENSION_VAR_ID &&
00418          variableID < 0))
00419     {
00420         EXCEPTION("variableID unknown");
00421     }
00422 
00423     if (mIsFixedDimensionSet)
00424     {
00425         if (dimensionPosition == -1 && variableID != UNLIMITED_DIMENSION_VAR_ID)
00426         {
00427             EXCEPTION("Dimension position not supplied");
00428         }
00429         if (dimensionPosition < -1 || dimensionPosition >= mFixedDimensionSize)
00430         {
00431             EXCEPTION("Dimension position out of range");
00432         }
00433         if (dimensionPosition != -1 && variableID == UNLIMITED_DIMENSION_VAR_ID)
00434         {
00435             EXCEPTION("Dimension position supplied, but not required");
00436         }
00437     }
00438 
00439     if (mIsUnlimitedDimensionSet)
00440     {
00441         if (mIsFixedDimensionSet)
00442         {
00443             // Go to the correct position in the file
00444             if (variableID == UNLIMITED_DIMENSION_VAR_ID)
00445             {
00446                 (*mpCurrentAncillaryFile) << std::endl << " ";
00447                 mpCurrentAncillaryFile->width(mFieldWidth);
00448                 (*mpCurrentAncillaryFile) << variableValue;
00449             }
00450             else
00451             {
00452                 int position;
00453                 if (variableID == FIXED_DIMENSION_VAR_ID)
00454                 {
00455                     position = mRowStartPosition + (mRowWidth+1) * dimensionPosition + SPACING - 1;
00456                 }
00457                 else
00458                 {
00459                     // ordinary variables
00460                     position = mRowStartPosition + (mRowWidth+1) * dimensionPosition +
00461                                ((variableID + (mpFixedDimensionVariable != NULL)) * (mFieldWidth + SPACING)) + SPACING - 1;
00462                 }
00463 
00464                 mpCurrentOutputFile->seekp(position);
00465                 mpCurrentOutputFile->width(mFieldWidth);
00466                 (*mpCurrentOutputFile) << variableValue;
00467             }
00468         }
00469         else
00470         {
00471             // Go to the correct position in the file
00472             int position;
00473             if (variableID == UNLIMITED_DIMENSION_VAR_ID)
00474             {
00475                 position = mRowStartPosition + SPACING - 1;
00476             }
00477             else
00478             {
00479                 position = (variableID + (mpUnlimitedDimensionVariable != NULL)) * (mFieldWidth + SPACING) +
00480                            mRowStartPosition + SPACING - 1;
00481             }
00482 
00483             mpCurrentOutputFile->seekp(position);
00484             mpCurrentOutputFile->width(mFieldWidth);
00485             (*mpCurrentOutputFile) << variableValue;
00486         }
00487     }
00488     else
00489     {
00490         // Go to the correct position in the file
00491         int position;
00492         if (variableID == FIXED_DIMENSION_VAR_ID)
00493         {
00494             position = mRowStartPosition + (mRowWidth+1) * dimensionPosition + SPACING - 1;
00495         }
00496         else
00497         {
00498             position = mRowStartPosition + (mRowWidth+1) * dimensionPosition +
00499                        ((variableID + (mpFixedDimensionVariable != NULL)) * (mFieldWidth + SPACING)) + SPACING - 1;
00500         }
00501         mpCurrentOutputFile->seekp(position);
00502         mpCurrentOutputFile->width(mFieldWidth);
00503         (*mpCurrentOutputFile) << variableValue;
00504     }
00505 
00506     mHasPutVariable = true;
00507 }
Generated on Thu Dec 22 13:00:07 2011 for Chaste by  doxygen 1.6.3