Chaste  Release::2017.1
ColumnDataWriter.cpp
1 /*
2 
3 Copyright (c) 2005-2017, University of Oxford.
4 All rights reserved.
5 
6 University of Oxford means the Chancellor, Masters and Scholars of the
7 University of Oxford, having an administrative office at Wellington
8 Square, Oxford OX1 2JD, UK.
9 
10 This file is part of Chaste.
11 
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions are met:
14  * Redistributions of source code must retain the above copyright notice,
15  this list of conditions and the following disclaimer.
16  * Redistributions in binary form must reproduce the above copyright notice,
17  this list of conditions and the following disclaimer in the documentation
18  and/or other materials provided with the distribution.
19  * Neither the name of the University of Oxford nor the names of its
20  contributors may be used to endorse or promote products derived from this
21  software without specific prior written permission.
22 
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 
34 */
35 
41 #include <ctype.h>
42 #include <sstream>
43 #include <iomanip>
44 #include <fstream>
45 
46 #include "ColumnDataWriter.hpp"
47 #include "ColumnDataConstants.hpp"
48 #include "Exception.hpp"
49 #include "Version.hpp"
50 
51 
52 ColumnDataWriter::ColumnDataWriter(const std::string& rDirectory,
53  const std::string& rBaseName,
54  bool cleanDirectory,
55  unsigned precision)
56  : mOutputFileHandler(rDirectory, cleanDirectory),
57  mDirectory(rDirectory),
58  mBaseName(rBaseName),
59  mIsInDefineMode(true),
60  mIsFixedDimensionSet(false),
61  mIsUnlimitedDimensionSet(false),
62  mUnlimitedDimensionPosition(0),
63  mFixedDimensionSize(-1),
64  mpCurrentOutputFile(nullptr),
65  mpCurrentAncillaryFile(nullptr),
66  mpUnlimitedDimensionVariable(nullptr),
67  mpFixedDimensionVariable(nullptr),
68  mFieldWidth(precision+8),
69  mPrecision(precision),
70  mHasPutVariable(false),
71  mNeedAdvanceAlongUnlimitedDimension(false),
72  mCommentForInfoFile("")
73 {
74  if (mPrecision<2 || mPrecision>20)
75  {
76  EXCEPTION("Precision must be between 2 and 20 (inclusive)");
77  }
78 }
79 
81 {
82  // Close any open output files
83  Close();
84 
85  // Delete memory allocated for variables
86  if (mpUnlimitedDimensionVariable != nullptr)
87  {
89  }
90  if (mpFixedDimensionVariable != nullptr)
91  {
93  }
94 }
95 
97 {
99 }
100 
102 {
103  if (mpCurrentOutputFile.get() != nullptr)
104  {
105  mpCurrentOutputFile->close();
106  mpCurrentOutputFile = out_stream(nullptr);
107  }
108 
109  if (mpCurrentAncillaryFile.get() != nullptr)
110  {
111  mpCurrentAncillaryFile->close();
112  mpCurrentAncillaryFile = out_stream(nullptr);
113  }
114 }
115 
116 void ColumnDataWriter::CheckVariableName(const std::string& rName)
117 {
118  if (rName.length() == 0)
119  {
120  EXCEPTION("Variable name not allowed: may not be blank.");
121  }
122  CheckUnitsName(rName);
123 }
124 
125 void ColumnDataWriter::CheckUnitsName(const std::string& rName)
126 {
127  for (unsigned i=0; i<rName.length(); i++)
128  {
129  if (!isalnum(rName[i]) && !(rName[i]=='_'))
130  {
131  std::string error = "Variable name/units '" + rName + "' not allowed: may only contain alphanumeric characters or '_'.";
132  EXCEPTION(error);
133  }
134  }
135 }
136 
137 int ColumnDataWriter::DefineUnlimitedDimension(const std::string& rDimensionName,
138  const std::string& rDimensionUnits)
139 {
141  {
142  EXCEPTION("Unlimited dimension already set. Cannot be defined twice");
143  }
144 
145  if (!mIsInDefineMode)
146  {
147  EXCEPTION("Cannot define variables when not in Define mode");
148  }
149 
150  CheckVariableName(rDimensionName);
151  CheckUnitsName(rDimensionUnits);
152 
153  mUnlimitedDimensionName = rDimensionName;
154  mUnlimitedDimensionUnits = rDimensionUnits;
155 
158  mpUnlimitedDimensionVariable->mVariableUnits = rDimensionUnits;
159 
161 
163 }
164 
165 int ColumnDataWriter::DefineFixedDimension(const std::string& rDimensionName,
166  const std::string& rDimensionUnits,
167  long dimensionSize)
168 {
169  if (!mIsInDefineMode)
170  {
171  EXCEPTION("Cannot define variables when not in Define mode");
172  }
173  if (dimensionSize < 1)
174  {
175  EXCEPTION("Fixed dimension must be at least 1 long");
176  }
177 
178  CheckVariableName(rDimensionName);
179  CheckUnitsName(rDimensionUnits);
180 
181  mFixedDimensionName = rDimensionName;
182  mFixedDimensionUnits = rDimensionUnits;
183  mFixedDimensionSize = dimensionSize;
184 
185  mIsFixedDimensionSet = true;
186 
188  mpFixedDimensionVariable->mVariableName = rDimensionName;
189  mpFixedDimensionVariable->mVariableUnits = rDimensionUnits;
190  return FIXED_DIMENSION_VAR_ID;
191 }
192 
193 int ColumnDataWriter::DefineVariable(const std::string& rVariableName,
194  const std::string& rVariableUnits)
195 {
196  if (!mIsInDefineMode)
197  {
198  EXCEPTION("Cannot define variables when not in Define mode");
199  }
200 
201  CheckVariableName(rVariableName);
202  CheckUnitsName(rVariableUnits);
203 
204  int variable_id;
205 
206  if (rVariableName == mUnlimitedDimensionName)
207  {
208  EXCEPTION("Variable name: " + rVariableName + " already in use as unlimited dimension");
209  }
210  else if (rVariableName == mFixedDimensionName)
211  {
212  EXCEPTION("Variable name: " + rVariableName + " already in use as fixed dimension");
213  }
214  else // ordinary variable
215  {
216  // Add the variable to the variable vector
217  DataWriterVariable new_variable;
218  new_variable.mVariableName = rVariableName;
219  new_variable.mVariableUnits = rVariableUnits;
220  mVariables.push_back(new_variable);
221 
222  // Use the index of the variable vector as the variable ID.
223  // This is ok since there is no way to remove variables.
224  variable_id = mVariables.size()-1;
225  }
226 
227  return variable_id;
228 }
229 
231 {
232  // Check that a dimension has been defined
233  if (mIsFixedDimensionSet == false && mIsUnlimitedDimensionSet == false)
234  {
235  EXCEPTION("Cannot end define mode. No dimensions have been defined.");
236  }
237  // Check that at least one variable has been defined
238  if (mVariables.size() < 1)
239  {
240  EXCEPTION("Cannot end define mode. No variables have been defined.");
241  }
242  // Calculate the width of each row
243  int unlimited_dimension_variable = (mpUnlimitedDimensionVariable != nullptr);
244  int fixed_dimension_variable = (mpFixedDimensionVariable != nullptr);
246  {
248  {
249  mRowWidth = (mVariables.size() + fixed_dimension_variable) * (mFieldWidth + SPACING);
251 
252  // Write out the headers for the first position along the unlimited dimension
253  std::stringstream suffix;
254  suffix << std::setfill('0') << std::setw(FILE_SUFFIX_WIDTH) << mUnlimitedDimensionPosition;
255 
256  if (mpUnlimitedDimensionVariable != nullptr)
257  {
258  std::string ancillary_filename = mBaseName + "_unlimited.dat";
259  mpCurrentAncillaryFile = mOutputFileHandler.OpenOutputFile(ancillary_filename, std::ios::out | std::ios::binary);
260  (*mpCurrentAncillaryFile) << std::setiosflags(std::ios::scientific);
261  (*mpCurrentAncillaryFile) << std::setprecision(mPrecision);
262  if (mpUnlimitedDimensionVariable != nullptr)
263  {
264  (*mpCurrentAncillaryFile) << mpUnlimitedDimensionVariable->mVariableName
265  << "(" << mpUnlimitedDimensionVariable->mVariableUnits << ") ";
266  }
267  }
269  std::string filename = mBaseName + "_" + suffix.str() + ".dat";
270  this->CreateFixedDimensionFile(filename);
271  }
272  else
273  {
274  mRowWidth = (mVariables.size() + unlimited_dimension_variable) * (mFieldWidth + SPACING);
275 
276  // Write out the column headers
277  std::string filename = mBaseName + ".dat";
278  mpCurrentOutputFile = mOutputFileHandler.OpenOutputFile(filename, std::ios::out);
279  (*mpCurrentOutputFile) << std::setiosflags(std::ios::scientific);
280  (*mpCurrentOutputFile) << std::setprecision(mPrecision);
281  if (mpUnlimitedDimensionVariable != nullptr)
282  {
283  (*mpCurrentOutputFile) << mpUnlimitedDimensionVariable->mVariableName
284  << "(" << mpUnlimitedDimensionVariable->mVariableUnits << ") ";
285  }
286  /*
287  * Write out header(which may contain several variables) for output file.
288  * In this scope the method "CreateFixedDimensionFile" has not been invoked,
289  * because there is no mFixedDimensionSize available.
290  */
291  for (unsigned i=0; i<mVariables.size(); i++)
292  {
293  (*mpCurrentOutputFile) << mVariables[i].mVariableName << "(" << mVariables[i].mVariableUnits << ")";
294  if (i < mVariables.size()-1)
295  {
296  (*mpCurrentOutputFile) << " ";
297  }
298  }
299  (*mpCurrentOutputFile) << std::endl;
301 
302  // Write out a line of blank space which is #variables * (mFieldWidth + 1) -1
303  std::string blank_line(mRowWidth, ' ');
304  (*mpCurrentOutputFile) << blank_line;
305  }
306  }
307  else
308  {
309  // The fixed dimension must be set at this point or we wouldn't be here
310  mRowWidth = (mVariables.size() + fixed_dimension_variable) * (mFieldWidth + SPACING);
311  std::string filename = mBaseName + ".dat";
312  this->CreateFixedDimensionFile(filename);
313  }
314 
315  // Write info file
316  std::string infoname = mBaseName + ".info";
317  this->CreateInfoFile(infoname);
318 
319  mIsInDefineMode = false;
320 }
321 
322 void ColumnDataWriter::CreateFixedDimensionFile(const std::string& rFileName)
323 {
324  // Create new data file
325  mpCurrentOutputFile = mOutputFileHandler.OpenOutputFile(rFileName, std::ios::out | std::ios::binary);
326  (*mpCurrentOutputFile) << std::setiosflags(std::ios::scientific);
327  (*mpCurrentOutputFile) << std::setprecision(mPrecision);
328  if (mpFixedDimensionVariable != nullptr)
329  {
330  (*mpCurrentOutputFile) << mpFixedDimensionVariable->mVariableName
331  << "(" << mpFixedDimensionVariable->mVariableUnits << ") ";
332  }
333  // Write out the column headers and spaces for the rest of the file
334  for (unsigned i = 0; i < mVariables.size(); i++)
335  {
336  (*mpCurrentOutputFile) << mVariables[i].mVariableName << "(" << mVariables[i].mVariableUnits << ")";
337  if (i < mVariables.size()-1)
338  {
339  (*mpCurrentOutputFile) << " ";
340  }
341  }
342  (*mpCurrentOutputFile) << std::endl;
344  std::string blank_line(mRowWidth, ' ');
345  for (int i = 0; i < mFixedDimensionSize; i++)
346  {
347  (*mpCurrentOutputFile) << blank_line << std::endl;
348  }
349 }
350 
351 void ColumnDataWriter::CreateInfoFile(const std::string& rFileName)
352 {
353  // Create new info file
354  out_stream p_info_file = mOutputFileHandler.OpenOutputFile(rFileName, std::ios::out | std::ios::binary);
355  (*p_info_file) << "FIXED " << mFixedDimensionSize << std::endl;
356  (*p_info_file) << "UNLIMITED " << mIsUnlimitedDimensionSet << std::endl;
357  (*p_info_file) << "VARIABLES " << mVariables.size() << std::endl;
358  if (mCommentForInfoFile != "")
359  {
360  *p_info_file << mCommentForInfoFile << std::endl;
361  }
362  *p_info_file << ChasteBuildInfo::GetProvenanceString();
363  p_info_file->close();
364 }
365 
367 {
368  mHasPutVariable = false;
370 
372  {
374  {
375  //first close the current file before creating another one
376  mpCurrentOutputFile->close();
377  std::stringstream suffix;
378  suffix << std::setfill('0') << std::setw(FILE_SUFFIX_WIDTH) << mUnlimitedDimensionPosition + 1;
379 
380  std::string filename = mBaseName + "_" + suffix.str() + ".dat";
381  this->CreateFixedDimensionFile(filename);
382  }
383  else
384  {
385  //go to the end of the current line
387  (*mpCurrentOutputFile) << std::endl;
389  std::string blank_line(mRowWidth,' ');
390  (*mpCurrentOutputFile) << blank_line;
391  }
392  }
393  else
394  {
395  EXCEPTION("Cannot advance along unlimited dimension if it is not defined");
396  }
398 }
399 
401 {
402  if (mHasPutVariable)
403  {
405  }
406 }
407 
408 void ColumnDataWriter::PutVariable(int variableID, double variableValue, long dimensionPosition)
409 {
410 
412  {
414  }
415 
416  // Check that we are not in define mode
417  if (mIsInDefineMode)
418  {
419  EXCEPTION("Cannot put variables when in Define mode");
420  }
421  // Check that variableID is in range (exception)
422  if (variableID > (int)mVariables.size() ||
423  (variableID != UNLIMITED_DIMENSION_VAR_ID &&
424  variableID != FIXED_DIMENSION_VAR_ID &&
425  variableID < 0))
426  {
427  EXCEPTION("variableID unknown");
428  }
429 
431  {
432  if (dimensionPosition == -1 && variableID != UNLIMITED_DIMENSION_VAR_ID)
433  {
434  EXCEPTION("Dimension position not supplied");
435  }
436  if (dimensionPosition < -1 || dimensionPosition >= mFixedDimensionSize)
437  {
438  EXCEPTION("Dimension position out of range");
439  }
440  if (dimensionPosition != -1 && variableID == UNLIMITED_DIMENSION_VAR_ID)
441  {
442  EXCEPTION("Dimension position supplied, but not required");
443  }
444  }
445 
447  {
449  {
450  // Go to the correct position in the file
451  if (variableID == UNLIMITED_DIMENSION_VAR_ID)
452  {
453  (*mpCurrentAncillaryFile) << std::endl << " ";
455  (*mpCurrentAncillaryFile) << variableValue;
456  }
457  else
458  {
459  int position;
460  if (variableID == FIXED_DIMENSION_VAR_ID)
461  {
462  position = mRowStartPosition + (mRowWidth+SPACING) * dimensionPosition;
463  }
464  else
465  {
466  // ordinary variables
467  position = mRowStartPosition + (mRowWidth+SPACING) * dimensionPosition +
468  ((variableID + (mpFixedDimensionVariable != nullptr)) * (mFieldWidth + SPACING));
469  }
470 
471  mpCurrentOutputFile->seekp(position);
473  (*mpCurrentOutputFile) << variableValue;
474  }
475  }
476  else
477  {
478  // Go to the correct position in the file
479  int position;
480  if (variableID == UNLIMITED_DIMENSION_VAR_ID)
481  {
482  position = mRowStartPosition;
483  }
484  else
485  {
486  position = (variableID + (mpUnlimitedDimensionVariable != nullptr)) * (mFieldWidth + SPACING) +
488  }
489 
490  mpCurrentOutputFile->seekp(position);
492  (*mpCurrentOutputFile) << variableValue;
493 
494  }
495  }
496  else
497  {
498  // Go to the correct position in the file
499  int position;
500  if (variableID == FIXED_DIMENSION_VAR_ID)
501  {
502  position = mRowStartPosition + (mRowWidth+SPACING) * dimensionPosition;
503  }
504  else
505  {
506  position = mRowStartPosition + (mRowWidth+SPACING) * dimensionPosition +
507  ((variableID + (mpFixedDimensionVariable != nullptr)) * (mFieldWidth + SPACING));
508  }
509  mpCurrentOutputFile->seekp(position);
511  (*mpCurrentOutputFile) << variableValue;
512  }
513 
514  mHasPutVariable = true;
515 }
void CheckVariableName(const std::string &rName)
static const int SPACING
out_stream mpCurrentOutputFile
virtual void PutVariable(int variableID, double variableValue, long dimensionPosition=-1)
int DefineUnlimitedDimension(const std::string &rDimensionName, const std::string &rDimensionUnits)
#define EXCEPTION(message)
Definition: Exception.hpp:143
virtual void AdvanceAlongUnlimitedDimension()
const unsigned mPrecision
void DoAdvanceAlongUnlimitedDimension()
virtual void Close()
std::vector< DataWriterVariable > mVariables
virtual void EndDefineMode()
static const int UNLIMITED_DIMENSION_VAR_ID
std::string GetOutputDirectory()
std::string GetOutputDirectoryFullPath() const
out_stream mpCurrentAncillaryFile
const unsigned mFieldWidth
DataWriterVariable * mpFixedDimensionVariable
out_stream OpenOutputFile(const std::string &rFileName, std::ios_base::openmode mode=std::ios::out|std::ios::trunc) const
virtual ~ColumnDataWriter()
ColumnDataWriter(const std::string &rDirectory, const std::string &rBaseName, bool cleanDirectory=true, unsigned precision=8)
void CreateFixedDimensionFile(const std::string &rFileName)
int DefineVariable(const std::string &rVariableName, const std::string &rVariableUnits)
int DefineFixedDimension(const std::string &rDimensionName, const std::string &rDimensionUnits, long dimensionSize)
std::string mCommentForInfoFile
std::string mFixedDimensionName
const int FILE_SUFFIX_WIDTH
std::string mFixedDimensionUnits
static std::string GetProvenanceString()
std::string mUnlimitedDimensionName
void CheckUnitsName(const std::string &rName)
std::string mUnlimitedDimensionUnits
static const int FIXED_DIMENSION_VAR_ID
void CreateInfoFile(const std::string &rFileName)
DataWriterVariable * mpUnlimitedDimensionVariable
bool mNeedAdvanceAlongUnlimitedDimension
OutputFileHandler mOutputFileHandler