Chaste Commit::baa90ac2819b962188b7562f2326be23c47859a7
ColumnDataWriter.cpp
1/*
2
3Copyright (c) 2005-2024, University of Oxford.
4All rights reserved.
5
6University of Oxford means the Chancellor, Masters and Scholars of the
7University of Oxford, having an administrative office at Wellington
8Square, Oxford OX1 2JD, UK.
9
10This file is part of Chaste.
11
12Redistribution and use in source and binary forms, with or without
13modification, 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
23THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32OF 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"
48#include "Exception.hpp"
49#include "Version.hpp"
50
51
52ColumnDataWriter::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
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
116void 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
125void 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
137int 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
159
161
163}
164
165int 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
186
188 mpFixedDimensionVariable->mVariableName = rDimensionName;
189 mpFixedDimensionVariable->mVariableUnits = rDimensionUnits;
191}
192
193int 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
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
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
322void 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
351void 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
407
408void 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}
const int FILE_SUFFIX_WIDTH
#define EXCEPTION(message)
static std::string GetProvenanceString()
void CreateInfoFile(const std::string &rFileName)
void CreateFixedDimensionFile(const std::string &rFileName)
void CheckUnitsName(const std::string &rName)
static const int SPACING
std::string mUnlimitedDimensionUnits
std::string mUnlimitedDimensionName
const unsigned mFieldWidth
static const int FIXED_DIMENSION_VAR_ID
virtual void EndDefineMode()
virtual void AdvanceAlongUnlimitedDimension()
OutputFileHandler mOutputFileHandler
int DefineVariable(const std::string &rVariableName, const std::string &rVariableUnits)
int DefineUnlimitedDimension(const std::string &rDimensionName, const std::string &rDimensionUnits)
DataWriterVariable * mpFixedDimensionVariable
std::string mCommentForInfoFile
DataWriterVariable * mpUnlimitedDimensionVariable
static const int UNLIMITED_DIMENSION_VAR_ID
bool mNeedAdvanceAlongUnlimitedDimension
out_stream mpCurrentOutputFile
out_stream mpCurrentAncillaryFile
std::string GetOutputDirectory()
void DoAdvanceAlongUnlimitedDimension()
int DefineFixedDimension(const std::string &rDimensionName, const std::string &rDimensionUnits, long dimensionSize)
void CheckVariableName(const std::string &rName)
std::string mFixedDimensionUnits
std::vector< DataWriterVariable > mVariables
std::string mFixedDimensionName
const unsigned mPrecision
ColumnDataWriter(const std::string &rDirectory, const std::string &rBaseName, bool cleanDirectory=true, unsigned precision=8)
virtual void PutVariable(int variableID, double variableValue, long dimensionPosition=-1)
std::string GetOutputDirectoryFullPath() const
out_stream OpenOutputFile(const std::string &rFileName, std::ios_base::openmode mode=std::ios::out|std::ios::trunc) const