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