Chaste Release::3.1
ColumnDataReader.cpp
Go to the documentation of this file.
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 
00042 #include "ColumnDataReader.hpp"
00043 #include "ColumnDataConstants.hpp"
00044 
00045 #include <fstream>
00046 #include <sstream>
00047 #include <iomanip>
00048 #include <cassert>
00049 #include <climits>
00050 #include "OutputFileHandler.hpp"
00051 #include "Exception.hpp"
00052 
00057 const int NOT_READ = -999;
00058 
00059 ColumnDataReader::ColumnDataReader(const std::string& rDirectory,
00060                                    const std::string& rBaseName,
00061                                    bool makeAbsolute)
00062 {
00063     // Find out where files are really stored
00064     std::string directory;
00065     if (makeAbsolute)
00066     {
00067         OutputFileHandler output_file_handler(rDirectory, false);
00068         directory = output_file_handler.GetOutputDirectoryFullPath();
00069     }
00070     else
00071     {
00072         // Add a trailing slash if needed
00073         if ( !(*(rDirectory.end()-1) == '/'))
00074         {
00075             directory = rDirectory + "/";
00076         }
00077     }
00078     CheckFiles(directory, rBaseName);
00079 }
00080 
00081 ColumnDataReader::ColumnDataReader(const FileFinder& rDirectory,
00082                                    const std::string& rBaseName)
00083 {
00084     if (!rDirectory.IsDir() || !rDirectory.Exists())
00085     {
00086         EXCEPTION("Directory does not exist: " + rDirectory.GetAbsolutePath());
00087     }
00088     CheckFiles(rDirectory.GetAbsolutePath(), rBaseName);
00089 }
00090 
00091 void ColumnDataReader::CheckFiles(const std::string& rDirectory, const std::string& rBaseName)
00092 {
00093     // Read in info file
00094     mInfoFilename = rDirectory + rBaseName + ".info";
00095     std::ifstream infofile(mInfoFilename.c_str(), std::ios::in);
00096 
00097     // If it doesn't exist - throw exception
00098     if (!infofile.is_open())
00099     {
00100         EXCEPTION("Couldn't open info file: " + mInfoFilename);
00101     }
00102     std::string junk;
00103     mNumFixedDimensions = NOT_READ;
00104     mHasUnlimitedDimension = false;
00105     mNumVariables = NOT_READ;
00106 
00107     infofile >> junk;
00108     infofile >> mNumFixedDimensions >> junk;
00109     infofile >> mHasUnlimitedDimension >> junk;
00110     infofile >> mNumVariables;
00111 
00112     if (mNumFixedDimensions == NOT_READ || mNumVariables == NOT_READ)
00113     {
00114         infofile.close();
00115         EXCEPTION("Couldn't read info file correctly");
00116     }
00117 
00118     // Read in variables and associated them with a column number
00119     if (mHasUnlimitedDimension)
00120     {
00121         if (mNumFixedDimensions < 1)
00122         {
00123             mDataFilename = rDirectory + rBaseName + ".dat";
00124         }
00125         else
00126         {
00127             std::stringstream suffix;
00128             suffix << std::setfill('0') << std::setw(FILE_SUFFIX_WIDTH) << 0;
00129 
00130             mDataFilename = rDirectory + rBaseName + "_" + suffix.str() + ".dat";
00131 
00132             /*
00133              * The ancillary path needs to come from a single place that is
00134              * used by both the reader & writer, otherwise all will be bad.
00135              */
00136             mAncillaryFilename = rDirectory + rBaseName + "_unlimited.dat";
00137 
00138             // Extract the units and place into map
00139             std::ifstream ancillaryfile(mAncillaryFilename.c_str(), std::ios::in);
00140 
00141             // If it doesn't exist - throw exception
00142             if (!ancillaryfile.is_open())
00143             {
00144                 EXCEPTION("Couldn't open ancillary data file");
00145             }
00146             std::string dimension;
00147             std::getline(ancillaryfile, dimension);
00148             std::stringstream dimension_stream(dimension);
00149             std::string dimension_unit, dimension_name, header;
00150             dimension_stream >> header;
00151 
00152             // Separate into variable name and units
00153             int unitpos = header.find("(") + 1;
00154 
00155             dimension_name = header.substr(0, unitpos - 1);
00156             dimension_unit = header.substr(unitpos, header.length() - unitpos - 1);
00157 
00158             mVariablesToUnits[dimension_name] = dimension_unit;
00159             ancillaryfile.close();
00160         }
00161     }
00162     else
00163     {
00164         mDataFilename = rDirectory + rBaseName + ".dat";
00165     }
00166 
00167     std::ifstream datafile(mDataFilename.c_str(), std::ios::in);
00168     // If it doesn't exist - throw exception
00169     if (!datafile.is_open())
00170     {
00171         EXCEPTION("Couldn't open data file");
00172     }
00173 
00174     std::string variables;
00175     std::getline(datafile, variables);
00176     std::stringstream variable_stream(variables);
00177     std::string header, variable, unit;
00178     int column = 0;
00179 
00180     // Insert variables into map
00181     while (variable_stream >> header)
00182     {
00183         // Separate into variable name and units
00184         int unitpos = header.find("(") + 1;
00185 
00186         variable = header.substr(0, unitpos - 1);
00187         unit = header.substr(unitpos, header.length() - unitpos - 1);
00188 
00189         mVariablesToColumns[variable] = column;
00190         mVariablesToUnits[variable] = unit;
00191 
00192         column++;
00193     }
00194 
00195     /*
00196      * Now read the first line of proper data to determine the field width used when this
00197      * file was created. Do this by reading the first entry and measuring the distance from
00198      * the decimal point to the 'e'.  This gives the precision; the field width is then
00199      * precision + 7.
00200      * e.g. if the first entry is
00201      *   6.3124e+01         => field width = 11 // chaste release 1 and 1.1
00202      *  -3.5124e+01         => field width = 11 // chaste release 1 and 1.1
00203      *  +1.00000000e+00     => field width = 15
00204      *  -1.20000000e+01     => field width = 15
00205      *  -1.12345678e-321    => field width = 15
00206      */
00207     std::string first_line;
00208     std::string first_entry;
00209 
00210     // Read the first entry of the line. If there is no first entry, move to the next line..
00211     while (first_entry.length()==0 && !datafile.eof())
00212     {
00213         std::getline(datafile, first_line);
00214         std::stringstream stream(first_line);
00215         stream >> first_entry;
00216     }
00217 
00218     if (datafile.eof() && first_entry.length()==0)
00219     {
00220         EXCEPTION("Unable to determine field width from file as cannot find any data entries");
00221     }
00222 
00223     size_t dot_pos = first_entry.find(".");
00224     size_t e_pos = first_entry.find("e");
00225     if (dot_pos == std::string::npos || e_pos == std::string::npos)
00226     {
00227         EXCEPTION("Badly formatted scientific data field");
00228     }
00229     mFieldWidth = e_pos - dot_pos - 1 + 7;
00230 
00231     // Attempt to account for old format files (which only allowed 2 characters for the exponent)
00232     dot_pos = first_line.find(".");
00233     size_t second_dot_pos = first_line.find(".", dot_pos+1);
00234     if ((second_dot_pos != std::string::npos) &&
00235         (second_dot_pos - dot_pos == mFieldWidth + SPACING - 1))
00236     {
00237         mFieldWidth--;
00238     }
00239 
00240     infofile.close();
00241     datafile.close();
00242 }
00243 
00244 std::vector<double> ColumnDataReader::GetValues(const std::string& rVariableName)
00245 {
00246     if (mNumFixedDimensions > 0)
00247     {
00248         EXCEPTION("Data file has fixed dimension which must be specified");
00249     }
00250 
00251     std::map<std::string, int>::iterator col = mVariablesToColumns.find(rVariableName);
00252     if (col == mVariablesToColumns.end())
00253     {
00254         std::stringstream variable_name;
00255         variable_name << rVariableName;
00256         EXCEPTION("'" + variable_name.str() + "' is an unknown variable.");
00257     }
00258 
00259     int column = (*col).second;
00260     ReadColumnFromFile(mDataFilename, column);
00261 
00262     return mValues;
00263 }
00264 
00265 std::vector<double> ColumnDataReader::GetValues(const std::string& rVariableName,
00266                                                 int fixedDimension)
00267 {
00268     if (mNumFixedDimensions < 1)
00269     {
00270         EXCEPTION("Data file has no fixed dimension");
00271     }
00272 
00273     mValues.clear();
00274     if (mHasUnlimitedDimension)
00275     {
00276         std::string datafile = mDataFilename;
00277         std::map<std::string, int>::iterator col = mVariablesToColumns.find(rVariableName);
00278         if (col == mVariablesToColumns.end())
00279         {
00280             EXCEPTION("Unknown variable");
00281         }
00282         int column = (*col).second;
00283 
00284         int counter = 1;
00285         while (true)
00286         {
00287             try
00288             {
00289                 ReadValueFromFile(datafile, column, fixedDimension);
00290             }
00291             catch (Exception)
00292             {
00293                 break;
00294             }
00295 
00296             // Advance counter
00297             std::string::size_type underscore_pos = datafile.rfind("_", datafile.length());
00298             std::stringstream suffix;
00299 
00300             suffix << std::setfill('0') << std::setw(FILE_SUFFIX_WIDTH) << counter;
00301 
00302             if (underscore_pos != std::string::npos)
00303             {
00304                 datafile = datafile.substr(0, underscore_pos+1) + suffix.str() + ".dat";
00305             }
00306             counter++;
00307         }
00308     }
00309     else
00310     {
00311         int column = mVariablesToColumns[rVariableName];
00312         if (0 == column)
00313         {
00314             EXCEPTION("Unknown variable");
00315         }
00316         ReadValueFromFile(mDataFilename, column, fixedDimension);
00317     }
00318 
00319     return mValues;
00320 }
00321 
00322 std::vector<double> ColumnDataReader::GetUnlimitedDimensionValues()
00323 {
00324     mValues.clear();
00325     if (!mHasUnlimitedDimension)
00326     {
00327         EXCEPTION("Data file has no unlimited dimension");
00328     }
00329     if (mNumFixedDimensions > 0)
00330     {
00331         // Read in from the ancillary file
00332         ReadColumnFromFile(mAncillaryFilename, 0);
00333     }
00334     else
00335     {
00336         // Read the first column
00337         ReadColumnFromFile(mDataFilename, 0);
00338     }
00339     return mValues;
00340 }
00341 
00342 void ColumnDataReader::ReadValueFromFile(const std::string& rFilename, int col, int row)
00343 {
00344     std::ifstream datafile(rFilename.c_str(), std::ios::in);
00345     // If it doesn't exist - throw exception
00346     if (!datafile.is_open())
00347     {
00348         EXCEPTION("Couldn't open data file");
00349     }
00350     std::string variable_values;
00351     for (int i=0; i<row+1; i++)
00352     {
00353         std::getline(datafile, variable_values);
00354     }
00355 
00356     std::getline(datafile, variable_values);
00357     this->PushColumnEntryFromLine(variable_values, col);
00358 
00359     datafile.close();
00360 }
00361 
00362 void ColumnDataReader::ReadColumnFromFile(const std::string& rFilename, int col)
00363 {
00364     // Empty the values vector
00365     mValues.clear();
00366 
00367     // Read in from the ancillary file
00368     std::ifstream datafile(rFilename.c_str(), std::ios::in);
00369     std::string value;
00370 
00371     // We should have already checked that this file can be opened.
00372     assert(datafile.is_open());
00373 
00374     // The current variable becomes true just after reading the last line
00375     bool end_of_file_reached = false;
00376 
00377     // Skip header line
00378     end_of_file_reached = std::getline(datafile, value).eof();
00379 
00380     while (!end_of_file_reached)
00381     {
00382         end_of_file_reached = std::getline(datafile, value).eof();
00383         this->PushColumnEntryFromLine(value, col);
00384     }
00385     datafile.close();
00386 }
00387 
00388 void ColumnDataReader::PushColumnEntryFromLine(const std::string& rLine, int col)
00389 {
00390     int startpos = col * (mFieldWidth + SPACING) + SPACING - 1;
00391     std::string value = rLine.substr(startpos, mFieldWidth + 1);
00392     std::stringstream variable_stream(value);
00393     double d_value;
00394     variable_stream >> d_value;
00395     if (variable_stream.fail())
00396     {
00397         d_value = DBL_MAX;
00398     }
00399 
00400     mValues.push_back(d_value);
00401 }
00402 
00403 bool ColumnDataReader::HasValues(const std::string& rVariableName)
00404 {
00405     std::map<std::string, int>::iterator col = mVariablesToColumns.find(rVariableName);
00406     return !(col == mVariablesToColumns.end());
00407 }
00408 
00409 unsigned ColumnDataReader::GetFieldWidth()
00410 {
00411     return mFieldWidth;
00412 }