XdmfWriter.cpp 20.1 KB
Newer Older
Kenneth Leiter's avatar
Kenneth Leiter committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*****************************************************************************/
/*                                    XDMF                                   */
/*                       eXtensible Data Model and Format                    */
/*                                                                           */
/*  Id : XdmfWriter.cpp                                                      */
/*                                                                           */
/*  Author:                                                                  */
/*     Kenneth Leiter                                                        */
/*     kenneth.leiter@arl.army.mil                                           */
/*     US Army Research Laboratory                                           */
/*     Aberdeen Proving Ground, MD                                           */
/*                                                                           */
/*     Copyright @ 2011 US Army Research Laboratory                          */
/*     All Rights Reserved                                                   */
/*     See Copyright.txt for details                                         */
/*                                                                           */
/*     This software is distributed WITHOUT ANY WARRANTY; without            */
/*     even the implied warranty of MERCHANTABILITY or FITNESS               */
/*     FOR A PARTICULAR PURPOSE.  See the above copyright notice             */
/*     for more information.                                                 */
/*                                                                           */
/*****************************************************************************/
23

24
#include <fstream>
25
#include <libxml/tree.h>
26
#include <sstream>
27
#include <utility>
28
#include "XdmfArray.hpp"
29
#include "XdmfInformation.hpp"
30
#include "XdmfHeavyDataWriter.hpp"
31
#include "XdmfHDF5Controller.hpp"
32
#include "XdmfHDF5Writer.hpp"
33
#include "XdmfItem.hpp"
34
#include "XdmfSystemUtils.hpp"
35
#include "XdmfWriter.hpp"
36
#include "XdmfVersion.hpp"
37
#include "XdmfError.hpp"
38

39 40 41
/**
 * PIMPL
 */
42
class XdmfWriter::XdmfWriterImpl {
43 44 45

public:

46
  XdmfWriterImpl(const std::string & xmlFilePath,
47 48
                 const shared_ptr<XdmfHeavyDataWriter> heavyDataWriter,
                 std::ostream * stream) :
49 50 51 52 53 54
    mDepth(0),
    mDocumentTitle("Xdmf"),
    mHeavyDataWriter(heavyDataWriter),
    mLastXPathed(false),
    mLightDataLimit(100),
    mMode(Default),
Kenneth Leiter's avatar
Kenneth Leiter committed
55
    mStream(stream),
56
    mWriteXPaths(true),
57
    mXPathParse(true),
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
    mXMLCurrentNode(NULL),
    mXMLDocument(NULL),
    mXMLFilePath(XdmfSystemUtils::getRealPath(xmlFilePath)),
    mXPathCount(0),
    mXPathString(""),
    mVersionString(XdmfVersion.getShort())
  {
  };

  ~XdmfWriterImpl()
  {
  };

  void
  closeFile()
  {
    mXPath.clear();
75

76
    // This section writes to file
77 78 79 80 81 82 83 84
    std::ofstream fileStream;
    if(!mStream) {
      fileStream.open(mXMLFilePath.c_str());
      mStream = &fileStream;
    }

    xmlBufferPtr buffer = xmlBufferCreate();
    xmlOutputBuffer * outputBuffer = xmlOutputBufferCreateBuffer(buffer,
85
                                                                 NULL);
86 87 88 89 90 91 92 93 94 95 96 97
    xmlSaveFormatFileTo(outputBuffer,
                        mXMLDocument,
                        "utf-8",
                        1);
    *mStream << buffer->content;
    xmlBufferFree(buffer);
    
    if(fileStream.is_open()) {
      fileStream.close();
      mStream = NULL;
    }
    
98 99
    xmlFreeDoc(mXMLDocument);
    xmlCleanupParser();
100

101 102 103
    if(mHeavyDataWriter->getMode() == XdmfHeavyDataWriter::Default) {
      mHeavyDataWriter->closeFile();
    }
104 105 106 107 108 109 110 111 112 113 114 115 116 117
  };

  void
  openFile()
  {
    mXMLDocument = xmlNewDoc((xmlChar*)"1.0");
    mXMLCurrentNode = xmlNewNode(NULL, (xmlChar*)mDocumentTitle.c_str());
    xmlNewProp(mXMLCurrentNode,
               (xmlChar*)"xmlns:xi",
               (xmlChar*)"http://www.w3.org/2001/XInclude");
    xmlNewProp(mXMLCurrentNode,
               (xmlChar*)"Version",
               (xmlChar*)mVersionString.c_str());
    xmlDocSetRootElement(mXMLDocument, mXMLCurrentNode);
118 119 120
    if(mHeavyDataWriter->getMode() == XdmfHeavyDataWriter::Default) {
      mHeavyDataWriter->openFile();
    }
121 122 123 124
  }

  int mDepth;
  std::string mDocumentTitle;
125
  shared_ptr<XdmfHeavyDataWriter> mHeavyDataWriter;
126 127 128
  bool mLastXPathed;
  unsigned int mLightDataLimit;
  Mode mMode;
129
  std::ostream * mStream;
130
  bool mWriteXPaths;
131
  bool mXPathParse;
132 133 134 135 136 137 138
  xmlNodePtr mXMLCurrentNode;
  xmlDocPtr mXMLDocument;
  std::string mXMLFilePath;
  std::map<const XdmfItem * const, std::string> mXPath;
  unsigned int mXPathCount;
  std::string mXPathString;
  std::string mVersionString;
139

140 141
};

142
shared_ptr<XdmfWriter>
143
XdmfWriter::New(const std::string & xmlFilePath)
144
{
145 146 147 148 149 150 151 152
  std::stringstream heavyFileName;
  size_t extension = xmlFilePath.rfind(".");
  if(extension != std::string::npos) {
    heavyFileName << xmlFilePath.substr(0, extension) << ".h5";
  }
  else {
    heavyFileName << xmlFilePath << ".h5";
  }
153
  shared_ptr<XdmfHDF5Writer> hdf5Writer = 
154
    XdmfHDF5Writer::New(heavyFileName.str());
155
  shared_ptr<XdmfWriter> p(new XdmfWriter(xmlFilePath, hdf5Writer));
156
  return p;
157 158
}

159
shared_ptr<XdmfWriter>
160
XdmfWriter::New(const std::string & xmlFilePath,
161
                const shared_ptr<XdmfHeavyDataWriter> heavyDataWriter)
162
{
163 164
  shared_ptr<XdmfWriter> p(new XdmfWriter(xmlFilePath,
                                          heavyDataWriter));
165
  return p;
166 167
}

168 169 170 171 172 173 174 175 176 177
shared_ptr<XdmfWriter> 
XdmfWriter::New(std::ostream & stream,
                const shared_ptr<XdmfHeavyDataWriter> heavyDataWriter)
{
  shared_ptr<XdmfWriter> p(new XdmfWriter("",
                                          heavyDataWriter,
                                          &stream));
  return p;
}

178
XdmfWriter::XdmfWriter(const std::string & xmlFilePath,
179 180 181 182 183
                       shared_ptr<XdmfHeavyDataWriter> heavyDataWriter,
                       std::ostream * stream) :
  mImpl(new XdmfWriterImpl(xmlFilePath, 
                           heavyDataWriter,
                           stream))
184 185 186
{
}

187
XdmfWriter::~XdmfWriter()
188
{
189
  delete mImpl;
190
}
191

192
shared_ptr<XdmfHeavyDataWriter>
193
XdmfWriter::getHeavyDataWriter()
194
{
195 196
  return boost::const_pointer_cast<XdmfHeavyDataWriter>
    (static_cast<const XdmfWriter &>(*this).getHeavyDataWriter());
197 198
}

199
shared_ptr<const XdmfHeavyDataWriter>
200
XdmfWriter::getHeavyDataWriter() const
201
{
202
  return mImpl->mHeavyDataWriter;
203 204
}

205 206
std::string
XdmfWriter::getFilePath() const
207
{
208
  return mImpl->mXMLFilePath;
209 210
}

211 212
unsigned int
XdmfWriter::getLightDataLimit() const
213
{
214
  return mImpl->mLightDataLimit;
215 216
}

217 218
XdmfWriter::Mode
XdmfWriter::getMode() const
219
{
220
  return mImpl->mMode;
221 222
}

223 224
bool
XdmfWriter::getWriteXPaths() const
225
{
226
  return mImpl->mWriteXPaths;
227 228
}

229 230 231 232 233 234
bool
XdmfWriter::getXPathParse() const
{
  return mImpl->mXPathParse;
}

235 236
void
XdmfWriter::setDocumentTitle(std::string title)
237
{
238
  mImpl->mDocumentTitle = title;
239
}
240

241 242 243 244 245 246
void
XdmfWriter::setHeavyDataWriter(shared_ptr<XdmfHeavyDataWriter> heavyDataWriter)
{
  mImpl->mHeavyDataWriter = heavyDataWriter;
}

247 248
void
XdmfWriter::setLightDataLimit(const unsigned int numValues)
249
{
250
  mImpl->mLightDataLimit = numValues;
251 252
}

253 254
void
XdmfWriter::setMode(const Mode mode)
255
{
256
  mImpl->mMode = mode;
257 258
}

259 260 261 262 263 264
void
XdmfWriter::setVersionString(std::string version)
{
  mImpl->mVersionString = version;
}

265 266
void
XdmfWriter::setWriteXPaths(const bool writeXPaths)
267
{
268
  mImpl->mWriteXPaths = writeXPaths;
269 270
}

271 272 273 274 275 276
void
XdmfWriter::setXPathParse(const bool xPathParse)
{
  mImpl->mXPathParse = xPathParse;
}

277 278
void
XdmfWriter::visit(XdmfArray & array,
279
                  const shared_ptr<XdmfBaseVisitor> visitor)
280
{
281 282 283 284 285
  if (mImpl->mDepth == 0) {
    mImpl->openFile();
  }
  mImpl->mDepth++;

Andrew J. Burns (Cont's avatar
Andrew J. Burns (Cont committed
286
  // Pull the Function and Subset accociated with the array
287
  shared_ptr<XdmfArrayReference> internalReference = array.getReference();
288

Andrew J. Burns (Cont's avatar
Andrew J. Burns (Cont committed
289 290
  // If in the correct read mode process the function or subset
  // if it exists
291
  if (internalReference && array.getReadMode() == XdmfArray::Reference) {
Andrew J. Burns (Cont's avatar
Andrew J. Burns (Cont committed
292 293
    // Pass information about the array to the function
    // so it can properly recreate it when read
294 295 296
    internalReference->setConstructedType(array.getItemTag());
    internalReference->setConstructedProperties(array.getItemProperties());
    internalReference->accept(visitor);
Andrew J. Burns (Cont's avatar
Andrew J. Burns (Cont committed
297 298 299 300 301
    // This does not write the data contained within the array to file
    // The data is regenerated upon read
  }
  else if (array.getReadMode() == XdmfArray::Controller) {
    // Controller mode is the default mode
302 303
    const bool isSubclassed = 
      array.getItemTag().compare(XdmfArray::ItemTag) != 0;
304

305
    if(isSubclassed) {
306
      try {
307
        this->visit(dynamic_cast<XdmfItem &>(array), visitor);
308 309 310 311
      }
      catch (XdmfError e) {
        throw e;
      }
312
    }
313

314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
    if(array.getSize() > 0 && !(mImpl->mLastXPathed && isSubclassed)) {
      std::vector<std::string> xmlTextValues;

      // Take care of writing to single heavy data file (Default behavior)
      if(!array.isInitialized() && array.getHeavyDataController(0) &&
         array.getHeavyDataController(0)->getFilePath().compare(mImpl->mHeavyDataWriter->getFilePath()) != 0 &&
         mImpl->mMode == Default) {
        array.read();
      }

      if(array.getHeavyDataController(0) ||
         array.getSize() > mImpl->mLightDataLimit) {
        // Write values to heavy data

        try {
          // This takes about half the time needed
          mImpl->mHeavyDataWriter->visit(array, mImpl->mHeavyDataWriter);
331
        }
332 333
        catch (XdmfError e) {
          throw e;
334
        }
335 336 337 338 339 340 341 342 343

        std::stringstream valuesStream;
        for(unsigned int i = 0; i < array.getNumberHeavyDataControllers(); ++i) {
          std::string heavyDataPath =
            array.getHeavyDataController(i)->getFilePath();
          size_t index = heavyDataPath.find_last_of("/\\");
          if(index != std::string::npos) {
            // If path is not a folder
            // put the directory path into this variable
344 345
            const std::string heavyDataDir = 
              heavyDataPath.substr(0, index + 1);
346 347 348 349 350 351 352 353
            // If the directory is in the XML File Path
            if(mImpl->mXMLFilePath.find(heavyDataDir) == 0) {
              heavyDataPath =
                heavyDataPath.substr(heavyDataDir.size(),
                                     heavyDataPath.size() - heavyDataDir.size());
              // Pull the file off of the end and place it in the DataPath
            }
            // Otherwise the full path is required
354
          }
355 356 357 358 359 360 361
          std::stringstream dimensionStream;
          for (unsigned int j = 0; j < array.getHeavyDataController(i)->getDimensions().size(); ++j) {
            dimensionStream << array.getHeavyDataController(i)->getDimensions()[j];
            if (j < array.getHeavyDataController(i)->getDimensions().size() - 1) {
              dimensionStream << " ";
            }
          }
362 363 364 365

          const std::string dataSetPath = 
            array.getHeavyDataController(i)->getDataSetPath();

366 367 368 369
          // Clear the stream
          valuesStream.str(std::string());
          if (array.getNumberHeavyDataControllers() > 1) {
            valuesStream << heavyDataPath << ":"
370
                         << dataSetPath
371 372 373 374 375 376
                         << "|" << dimensionStream.str();
            if (i + 1 < array.getNumberHeavyDataControllers()){
              valuesStream << "|";
            }
          }
          else {
377 378 379 380
            valuesStream << heavyDataPath;
            if(!dataSetPath.empty()) {
              valuesStream << ":" << dataSetPath;
            }
381 382
          }
          xmlTextValues.push_back(valuesStream.str());
383
        }
384
      }
385 386 387 388
      else {
        // Write values to XML
        xmlTextValues.push_back(array.getValuesString());
      }
389

390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
      bool oldWriteXPaths = mImpl->mWriteXPaths;
      mImpl->mWriteXPaths = false;

      // Write XML (metadata) description
      if(isSubclassed) {
        shared_ptr<XdmfArray> arrayToWrite = XdmfArray::New();
        array.swap(arrayToWrite);
        mImpl->mXMLCurrentNode = mImpl->mXMLCurrentNode->last;
        this->visit(dynamic_cast<XdmfItem &>(*arrayToWrite.get()), visitor);
        for(unsigned int i = 0; i<xmlTextValues.size(); ++i) {
          xmlAddChild(mImpl->mXMLCurrentNode->last,
                      xmlNewText((xmlChar*)xmlTextValues[i].c_str()));
        }
        mImpl->mXMLCurrentNode = mImpl->mXMLCurrentNode->parent;
        array.swap(arrayToWrite);
405
      }
406 407 408 409 410 411
      else {
        this->visit(dynamic_cast<XdmfItem &>(array), visitor);
        for(unsigned int i = 0; i<xmlTextValues.size(); ++i) {
          xmlAddChild(mImpl->mXMLCurrentNode->last,
                      xmlNewText((xmlChar*)xmlTextValues[i].c_str()));
        }
412
      }
413
      mImpl->mWriteXPaths = oldWriteXPaths;
414 415 416
    }

  }
Andrew J. Burns (Cont's avatar
Andrew J. Burns (Cont committed
417 418 419
  else {
    // These statements are reached when an unsupported read mode is used
    // or when a read mode is not properly set up
420
    if (array.getReadMode() == XdmfArray::Reference) {
Andrew J. Burns (Cont's avatar
Andrew J. Burns (Cont committed
421 422
      try {
        XdmfError::message(XdmfError::FATAL,
423 424
                           "Error: Array to be output as an array reference"
                           " does not have an associated reference.");
Andrew J. Burns (Cont's avatar
Andrew J. Burns (Cont committed
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
      }
      catch (XdmfError e) {
        throw e;
      }
    }
    else {
      try {
        XdmfError::message(XdmfError::FATAL,
                           "Error: Invalid output type.");
      }
      catch (XdmfError e) {
        throw e;
      }
    }
  }
440 441 442 443 444

  mImpl->mDepth--;
  if(mImpl->mDepth <= 0) {
    mImpl->closeFile();
  }
445 446
}

447 448
void
XdmfWriter::visit(XdmfItem & item,
449
                  const shared_ptr<XdmfBaseVisitor> visitor)
450
{
451

452 453 454 455 456 457 458
  if (mImpl->mDepth == 0) {
    mImpl->openFile();
  }
  mImpl->mDepth++;

  std::string tag = item.getItemTag();
  if (tag.length() == 0) {
459 460 461 462 463 464
    try {
      item.traverse(visitor);
    }
    catch (XdmfError e) {
      throw e;
    }
465 466 467
  }
  else {
    if(mImpl->mWriteXPaths) {
468 469 470 471
      if (tag == "Information" && mImpl->mXPathParse) {
        XdmfInformation & xpathinfo = dynamic_cast<XdmfInformation &>(item);
        if (xpathinfo.getKey() == "XIncludes") {
          shared_ptr<XdmfInformation> outputinfo;
472
          for (unsigned int i = 0; i < xpathinfo.getNumberInformations(); ++i) {
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
            mImpl->mXPathCount++;
            outputinfo = xpathinfo.getInformation(i);
            mImpl->mXMLCurrentNode = xmlNewChild(mImpl->mXMLCurrentNode,
                                                 NULL,
                                                 (xmlChar*)"xi:include",
                                                 NULL);
            xmlNewProp(mImpl->mXMLCurrentNode,
                       (xmlChar*)"href",
                       (xmlChar*)(outputinfo->getKey().c_str()));
            xmlNewProp(mImpl->mXMLCurrentNode,
                       (xmlChar*)"xpointer",
                       (xmlChar*)(outputinfo->getValue().c_str()));
            if (i < xpathinfo.getNumberInformations()-1) {
              mImpl->mXMLCurrentNode = mImpl->mXMLCurrentNode->parent;
            }
          }
        }
        else {
          mImpl->mXPathCount++;

          const std::string parentXPathString = mImpl->mXPathString;

          std::stringstream newXPathString;
          newXPathString << mImpl->mXPathString << "/" << mImpl->mXPathCount;
          mImpl->mXPathString = newXPathString.str();

          std::map<const XdmfItem * const, std::string>::const_iterator iter =
            mImpl->mXPath.find(&item);
          if(iter != mImpl->mXPath.end()) {
            // Inserted before --- just xpath location of previously written node
            mImpl->mXMLCurrentNode = xmlNewChild(mImpl->mXMLCurrentNode,
                                                 NULL,
                                                 (xmlChar*)"xi:include",
                                                 NULL);
            xmlNewProp(mImpl->mXMLCurrentNode,
                       (xmlChar*)"xpointer",
                       (xmlChar*)iter->second.c_str());
            mImpl->mLastXPathed = true;
          }
          else {
            // Not inserted before --- need to write all data and traverse.
            mImpl->mXMLCurrentNode = xmlNewChild(mImpl->mXMLCurrentNode,
                                                 NULL,
                                                 (xmlChar *)tag.c_str(),
                                                 NULL);
            std::stringstream xPathProp;
            xPathProp << "element(/1" << mImpl->mXPathString << ")";
            mImpl->mXPath.insert(std::make_pair(&item, xPathProp.str()));
            const std::map<std::string, std::string> & itemProperties =
              item.getItemProperties();
            for(std::map<std::string, std::string>::const_iterator iter =
                itemProperties.begin();
                iter != itemProperties.end();
                ++iter) {
              xmlNewProp(mImpl->mXMLCurrentNode,
                         (xmlChar*)iter->first.c_str(),
                         (xmlChar*)iter->second.c_str());
            }
            const unsigned int parentCount = mImpl->mXPathCount;
            mImpl->mXPathCount = 0;
            item.traverse(visitor);
            mImpl->mXPathCount = parentCount;
            mImpl->mLastXPathed = false;
          }

          mImpl->mXPathString = parentXPathString;
539

540 541 542 543
        }
      }
      else {
        mImpl->mXPathCount++;
544

545
        const std::string parentXPathString = mImpl->mXPathString;
546

547 548 549 550 551
        std::stringstream newXPathString;
        newXPathString << mImpl->mXPathString << "/" << mImpl->mXPathCount;
        mImpl->mXPathString = newXPathString.str();

        std::map<const XdmfItem * const, std::string>::const_iterator iter =
552
        mImpl->mXPath.find(&item);
553 554 555 556 557 558 559 560 561 562 563 564 565
        if(iter != mImpl->mXPath.end()) {
          // Inserted before --- just xpath location of previously written node
          mImpl->mXMLCurrentNode = xmlNewChild(mImpl->mXMLCurrentNode,
                                               NULL,
                                               (xmlChar*)"xi:include",
                                               NULL);
         xmlNewProp(mImpl->mXMLCurrentNode,
                    (xmlChar*)"xpointer",
                    (xmlChar*)iter->second.c_str());
         mImpl->mLastXPathed = true;
        }
        else {
          // Not inserted before --- need to write all data and traverse.
566

567 568 569 570 571 572 573 574
          mImpl->mXMLCurrentNode = xmlNewChild(mImpl->mXMLCurrentNode,
                                               NULL,
                                               (xmlChar *)tag.c_str(),
                                               NULL);
          std::stringstream xPathProp;
          xPathProp << "element(/1" << mImpl->mXPathString << ")";
          mImpl->mXPath.insert(std::make_pair(&item, xPathProp.str()));
          const std::map<std::string, std::string> & itemProperties =
575
          item.getItemProperties();
576
          for(std::map<std::string, std::string>::const_iterator iter =
577
              itemProperties.begin();
578 579 580 581 582 583 584 585 586 587 588
              iter != itemProperties.end();
              ++iter) {
            xmlNewProp(mImpl->mXMLCurrentNode,
                       (xmlChar*)iter->first.c_str(),
                       (xmlChar*)iter->second.c_str());
          }
          const unsigned int parentCount = mImpl->mXPathCount;
          mImpl->mXPathCount = 0;
          item.traverse(visitor);
          mImpl->mXPathCount = parentCount;
          mImpl->mLastXPathed = false;
589
        }
590
        mImpl->mXPathString = parentXPathString;
591 592 593
      }
    }
    else
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
    {
     // Not inserted before --- need to write all data and traverse.
      mImpl->mXMLCurrentNode = xmlNewChild(mImpl->mXMLCurrentNode,
                                           NULL,
                                           (xmlChar*)tag.c_str(),
                                           NULL);
      const std::map<std::string, std::string> itemProperties =
        item.getItemProperties();
      for(std::map<std::string, std::string>::const_iterator iter =
            itemProperties.begin();
          iter != itemProperties.end();
          ++iter) {
        xmlNewProp(mImpl->mXMLCurrentNode,
                   (xmlChar*)iter->first.c_str(),
                   (xmlChar*)iter->second.c_str());
609
      }
610 611 612 613 614 615
      try {
        item.traverse(visitor);
      }
      catch (XdmfError e) {
        throw e;
      }
616
    }
617 618 619 620 621 622 623 624

    mImpl->mXMLCurrentNode = mImpl->mXMLCurrentNode->parent;
  }

  mImpl->mDepth--;
  if(mImpl->mDepth <= 0) {
    mImpl->closeFile();
  }
625
}