1 /*
2  MDAL - Mesh Data Abstraction Library (MIT License)
3  Copyright (C) 2018 Lutra Consulting Limited
4 */
5 
6 #include <vector>
7 #include <string>
8 #include <string.h>
9 #include <sstream>
10 #include <iostream>
11 #include <fstream>
12 #include <memory>
13 #include <algorithm>
14 #include <map>
15 #include <cmath>
16 #include <limits>
17 
18 #include "mdal_xdmf.hpp"
19 #include "mdal_utils.hpp"
20 #include "mdal_data_model.hpp"
21 #include "mdal_xml.hpp"
22 #include "mdal_logger.hpp"
23 
XdmfDataset(MDAL::DatasetGroup * grp,const MDAL::HyperSlab & slab,const HdfDataset & valuesDs,RelativeTimestamp time)24 MDAL::XdmfDataset::XdmfDataset( MDAL::DatasetGroup *grp,
25                                 const MDAL::HyperSlab &slab,
26                                 const HdfDataset &valuesDs,
27                                 RelativeTimestamp time )
28   : MDAL::Dataset2D( grp )
29   , mHdf5DatasetValues( valuesDs )
30   , mHyperSlab( slab )
31 {
32   setTime( time );
33 }
34 
35 MDAL::XdmfDataset::~XdmfDataset() = default;
36 
offsets(size_t indexStart)37 std::vector<hsize_t> MDAL::XdmfDataset::offsets( size_t indexStart )
38 {
39   std::vector<hsize_t> ret( 2 );
40   ret[0] = mHyperSlab.startX + indexStart;
41   ret[1] = mHyperSlab.startY;
42   return ret;
43 }
44 
selections(size_t copyValues)45 std::vector<hsize_t> MDAL::XdmfDataset::selections( size_t copyValues )
46 {
47   std::vector<hsize_t> ret( 2 );
48   if ( mHyperSlab.countInFirstColumn )
49   {
50     ret[0] = copyValues;
51     ret[1] = mHyperSlab.isScalar ? 1 : 3;
52   }
53   else
54   {
55     ret[0] = mHyperSlab.isScalar ? 1 : 3;
56     ret[1] = copyValues;
57   }
58   return ret;
59 }
60 
scalarData(size_t indexStart,size_t count,double * buffer)61 size_t MDAL::XdmfDataset::scalarData( size_t indexStart, size_t count, double *buffer )
62 {
63   assert( group()->isScalar() ); //checked in C API interface
64   assert( mHyperSlab.isScalar );
65 
66   size_t nValues = mHyperSlab.count;
67   if ( ( count < 1 ) || ( indexStart >= nValues ) )
68     return 0;
69   size_t copyValues = std::min( nValues - indexStart, count );
70 
71   std::vector<hsize_t> off = offsets( indexStart );
72   std::vector<hsize_t> counts = selections( copyValues );
73   std::vector<double> values = mHdf5DatasetValues.readArrayDouble( off, counts );
74   if ( values.empty() )
75     return 0;
76 
77   const double *input = values.data();
78   memcpy( buffer, input, copyValues * sizeof( double ) );
79   return copyValues;
80 }
81 
82 
vectorData(size_t indexStart,size_t count,double * buffer)83 size_t MDAL::XdmfDataset::vectorData( size_t indexStart, size_t count, double *buffer )
84 {
85   assert( !group()->isScalar() ); //checked in C API interface
86   assert( !mHyperSlab.isScalar );
87 
88   size_t nValues = mHyperSlab.count;
89   if ( ( count < 1 ) || ( indexStart >= nValues ) )
90     return 0;
91   size_t copyValues = std::min( nValues - indexStart, count );
92 
93   std::vector<hsize_t> off = offsets( indexStart );
94   std::vector<hsize_t> counts = selections( copyValues );
95   std::vector<double> values = mHdf5DatasetValues.readArrayDouble( off, counts );
96   if ( values.empty() )
97     return 0;
98 
99   const double *input = values.data();
100   for ( size_t j = 0; j < copyValues; ++j )
101   {
102     buffer[2 * j] = input[3 * j];
103     buffer[2 * j + 1] = input[3 * j + 1];
104   }
105   return copyValues;
106 }
107 
108 // //////////////////////////////////////////////////////////////////////////////
109 // //////////////////////////////////////////////////////////////////////////////
110 // //////////////////////////////////////////////////////////////////////////////
111 
112 
XdmfFunctionDataset(MDAL::DatasetGroup * grp,MDAL::XdmfFunctionDataset::FunctionType type,const RelativeTimestamp & time)113 MDAL::XdmfFunctionDataset::XdmfFunctionDataset(
114   MDAL::DatasetGroup *grp,
115   MDAL::XdmfFunctionDataset::FunctionType type,
116   const RelativeTimestamp &time )
117   : MDAL::Dataset2D( grp )
118   , mType( type )
119   , mBaseReferenceGroup( "XDMF", grp->mesh(), grp->uri() )
120 {
121   setTime( time );
122   mBaseReferenceGroup.setIsScalar( true );
123   mBaseReferenceGroup.setDataLocation( grp->dataLocation() );
124   mBaseReferenceGroup.setName( "Base group for reference datasets" );
125 }
126 
127 MDAL::XdmfFunctionDataset::~XdmfFunctionDataset() = default;
128 
addReferenceDataset(const HyperSlab & slab,const HdfDataset & hdfDataset,const MDAL::RelativeTimestamp & time)129 void MDAL::XdmfFunctionDataset::addReferenceDataset(
130   const HyperSlab &slab,
131   const HdfDataset &hdfDataset,
132   const MDAL::RelativeTimestamp &time )
133 {
134   std::shared_ptr<MDAL::XdmfDataset> xdmfDataset = std::make_shared<MDAL::XdmfDataset>(
135         &mBaseReferenceGroup,
136         slab,
137         hdfDataset,
138         time
139       );
140   mReferenceDatasets.push_back( xdmfDataset );
141 }
142 
swap()143 void MDAL::XdmfFunctionDataset::swap()
144 {
145   if ( mReferenceDatasets.size() < 2 )
146     return;
147   std::swap( mReferenceDatasets[0], mReferenceDatasets[1] );
148 }
149 
scalarData(size_t indexStart,size_t count,double * buffer)150 size_t MDAL::XdmfFunctionDataset::scalarData( size_t indexStart, size_t count, double *buffer )
151 {
152   assert( group()->isScalar() ); //checked in C API interface
153   assert( mType != FunctionType::Join );
154 
155   if ( mType == FunctionType::Subtract )
156     return subtractFunction( indexStart, count, buffer );
157 
158   if ( mType == FunctionType::Flow )
159     return flowFunction( indexStart, count, buffer );
160 
161   return 0;
162 }
163 
vectorData(size_t indexStart,size_t count,double * buffer)164 size_t MDAL::XdmfFunctionDataset::vectorData( size_t indexStart, size_t count, double *buffer )
165 {
166   assert( !group()->isScalar() ); //checked in C API interface
167   assert( mType == FunctionType::Join );
168 
169   return joinFunction( indexStart, count, buffer );
170 }
171 
subtractFunction(size_t indexStart,size_t count,double * buffer)172 size_t MDAL::XdmfFunctionDataset::subtractFunction( size_t indexStart, size_t count, double *buffer )
173 {
174   std::vector<double> buf( 2 * count, std::numeric_limits<double>::quiet_NaN() );
175   size_t copyVals = extractRawData( indexStart, count, 2, buf );
176   for ( size_t j = 0; j < copyVals; ++j )
177   {
178     double x0 = buf[j];
179     double x1 = buf[count + j];
180     if ( !std::isnan( x0 ) && !std::isnan( x1 ) )
181       buffer[j] = x1 - x0;
182   }
183   return copyVals;
184 }
185 
flowFunction(size_t indexStart,size_t count,double * buffer)186 size_t MDAL::XdmfFunctionDataset::flowFunction( size_t indexStart, size_t count, double *buffer )
187 {
188   std::vector<double> buf( 4 * count, std::numeric_limits<double>::quiet_NaN() );
189   size_t copyVals = extractRawData( indexStart, count, 4, buf );
190   for ( size_t j = 0; j < copyVals; ++j )
191   {
192     double x0 = buf[1 * count + j];
193     double x1 = buf[1 * count + j];
194     double x2 = buf[2 * count + j];
195     double x3 = buf[3 * count + j];
196 
197     if ( !std::isnan( x0 ) &&
198          !std::isnan( x1 ) &&
199          !std::isnan( x2 ) &&
200          !MDAL::equals( x2, x3 )
201        )
202     {
203       double diff = x2 - x3;
204       buffer[j] = sqrt( ( x0 / diff ) * ( x0 / diff ) + ( x1 / diff ) * ( x1 / diff ) );
205     }
206   }
207   return copyVals;
208 }
209 
joinFunction(size_t indexStart,size_t count,double * buffer)210 size_t MDAL::XdmfFunctionDataset::joinFunction( size_t indexStart, size_t count, double *buffer )
211 {
212   std::vector<double> buf( 2 * count, std::numeric_limits<double>::quiet_NaN() );
213   size_t copyVals = extractRawData( indexStart, count, 2, buf );
214   for ( size_t j = 0; j < copyVals; ++j )
215   {
216     double x = buf[j];
217     double y = buf[count + j];
218     if ( !std::isnan( x ) && !std::isnan( y ) )
219     {
220       buffer[2 * j] = x;
221       buffer[2 * j + 1] = y;
222     }
223   }
224   return copyVals;
225 }
226 
extractRawData(size_t indexStart,size_t count,size_t nDatasets,std::vector<double> & buf)227 size_t MDAL::XdmfFunctionDataset::extractRawData( size_t indexStart, size_t count, size_t nDatasets, std::vector< double > &buf )
228 {
229   assert( buf.size() == nDatasets * count );
230 
231   if ( mReferenceDatasets.size() < nDatasets )
232     return 0;
233 
234   if ( !mReferenceDatasets[0]->group()->isScalar() )
235     return 0;
236 
237   size_t ret = mReferenceDatasets[0]->scalarData( indexStart, count, buf.data() );
238   for ( size_t i = 1; i < nDatasets ; ++i )
239   {
240     if ( !mReferenceDatasets[i]->group()->isScalar() )
241       return 0;
242     size_t ret1 = mReferenceDatasets[i]->scalarData( indexStart, count, buf.data() + count * i );
243     if ( ret != ret1 )
244       return 0;
245   }
246   return ret;
247 }
248 
249 // //////////////////////////////////////////////////////////////////////////////
250 // //////////////////////////////////////////////////////////////////////////////
251 // //////////////////////////////////////////////////////////////////////////////
252 
parseHyperSlab(const std::string & str,size_t dimX)253 MDAL::HyperSlab MDAL::DriverXdmf::parseHyperSlab( const std::string &str, size_t dimX )
254 {
255   std::stringstream slabSS( str );
256   std::vector<std::vector<size_t>> data( 3, std::vector<size_t>( dimX ) );
257   size_t i = 0;
258   size_t number;
259   while ( slabSS >> number )
260   {
261     data[i / dimX][i % dimX] = number;
262     i++;
263   }
264   if ( i != 3 * dimX )
265   {
266     throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "hyperSlab dimensions mismatch" );
267   }
268 
269   MDAL::HyperSlab slab;
270   slab.startX = data[0][0];
271   slab.startY = data[0][1];
272   size_t countX = data[2][0];
273   size_t countY = data[2][1];
274 
275   if ( ( data[1][0] != 1 ) || ( data[1][1] != 1 ) )
276   {
277     throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "only hyperSlab with stride 1 are supported" );
278   }
279 
280   // sort
281   if ( ( countX < countY ) && ( countY != 3 ) )
282   {
283     std::swap( countX, countY );
284     slab.countInFirstColumn = false;
285   }
286   else
287   {
288     slab.countInFirstColumn = true;
289   }
290   slab.count = countX;
291 
292   if ( countY == 1 )
293   {
294     slab.isScalar = true;
295   }
296   else if ( countY == 3 )
297   {
298     slab.isScalar = false;
299   }
300   else
301   {
302     throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "hyperSlab dimensions mismatch, not scalar or vector" );
303   }
304 
305   return slab;
306 }
307 
parseHyperSlabNode(const XMLFile & xmfFile,xmlNodePtr node)308 MDAL::HyperSlab MDAL::DriverXdmf::parseHyperSlabNode( const XMLFile &xmfFile, xmlNodePtr node )
309 {
310   std::string slabDimS = xmfFile.attribute( node, "Dimensions" );
311   std::vector<size_t> slabDim = parseDimensions2D( slabDimS );
312   if ( slabDim[0] != 3 || ( slabDim[1] != 2 && slabDim[1] != 3 ) )
313   {
314     throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Only two-dimensional slab array with dim 3x3 is supported (1)" );
315   }
316 
317   std::string slabS = xmfFile.content( node );
318   HyperSlab slab = parseHyperSlab( slabS, slabDim[1] );
319   return slab;
320 }
321 
parseHdf5Node(const XMLFile & xmfFile,xmlNodePtr node)322 HdfDataset MDAL::DriverXdmf::parseHdf5Node( const XMLFile &xmfFile, xmlNodePtr node )
323 {
324   std::string snapshotDimS = xmfFile.attribute( node, "Dimensions" );
325   std::vector<size_t> snapshotDim = parseDimensions2D( snapshotDimS );
326 
327   std::string hdf5Name, hdf5Path;
328   hdf5NamePath( xmfFile.content( node ), hdf5Name, hdf5Path );
329 
330   std::shared_ptr<HdfFile> hdfFile;
331   if ( mHdfFiles.count( hdf5Name ) == 0 )
332   {
333     hdfFile = std::make_shared<HdfFile>( hdf5Name, HdfFile::ReadOnly );
334     mHdfFiles[hdf5Name] = hdfFile;
335   }
336   else
337   {
338     hdfFile = mHdfFiles[hdf5Name];
339   }
340 
341   HdfDataset hdfDataset( hdfFile->id(), hdf5Path );
342   return hdfDataset;
343 }
344 
hdf5NamePath(const std::string & dataItemPath,std::string & filePath,std::string & hdf5Path)345 void MDAL::DriverXdmf::hdf5NamePath( const std::string &dataItemPath, std::string &filePath, std::string &hdf5Path )
346 {
347   std::string dirName = MDAL::dirName( mDatFile );
348   std::string path( dataItemPath );
349   size_t endpos = path.find_last_not_of( " \t\n" );
350   if ( std::string::npos != endpos )
351   {
352     path.erase( endpos + 1 );
353   }
354   size_t startpos = path.find_first_not_of( " \t\n" );
355   if ( std::string::npos != startpos )
356   {
357     path.erase( 0, startpos );
358   }
359 
360   std::vector<std::string> chunks = MDAL::split( path, ":" );
361   if ( chunks.size() != 2 )
362   {
363     throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "must be in format fileName:hdfPath" );
364   }
365 
366   filePath = dirName + "/" + chunks[0];
367   hdf5Path = chunks[1];
368 }
369 
parseDimensions2D(const std::string & data)370 std::vector<size_t> MDAL::DriverXdmf::parseDimensions2D( const std::string &data )
371 {
372   std::stringstream slabDimSS( data );
373   std::vector<size_t> slabDim;
374   size_t number;
375   while ( slabDimSS >> number )
376     slabDim.push_back( number );
377   if ( slabDim.size() != 2 )
378   {
379     throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Only two-dimensional slab array is supported" );
380   }
381   return slabDim;
382 }
383 
parseXdmfDataset(const XMLFile & xmfFile,xmlNodePtr itemNod)384 std::pair<HdfDataset, MDAL::HyperSlab> MDAL::DriverXdmf::parseXdmfDataset(
385   const XMLFile &xmfFile,
386   xmlNodePtr itemNod )
387 {
388   size_t facesCount = mMesh->facesCount();
389 
390   size_t dim = xmfFile.querySizeTAttribute( itemNod, "Dimensions" );
391   if ( dim != facesCount )
392   {
393     throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Dataset dimensions should correspond to the number of mesh elements" );
394   }
395 
396   xmlNodePtr node1 = xmfFile.getCheckChild( itemNod, "DataItem" );
397   xmlNodePtr node2 = xmfFile.getCheckSibling( node1, "DataItem" );
398 
399   std::string format1 = xmfFile.attribute( node1, "Format" );
400   std::string format2 = xmfFile.attribute( node2, "Format" );
401 
402   if ( ( format1 == "XML" ) && ( format2 == "HDF" ) )
403   {
404     HyperSlab slab = parseHyperSlabNode( xmfFile, node1 );
405     HdfDataset hdfDataset = parseHdf5Node( xmfFile, node2 );
406     return std::make_pair( hdfDataset, slab );
407   }
408   else
409   {
410     throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Only XML hyperSlab and HDF dataset Format supported" );
411   }
412 }
413 
parseXdmfXml()414 MDAL::DatasetGroups MDAL::DriverXdmf::parseXdmfXml( )
415 {
416   std::map< std::string, std::shared_ptr<MDAL::DatasetGroup> > groups;
417   size_t nTimesteps = 0;
418 
419   XMLFile xmfFile;
420   xmfFile.openFile( mDatFile );
421 
422   xmlNodePtr elem = xmfFile.getCheckRoot( "Xdmf" );
423   elem = xmfFile.getCheckChild( elem, "Domain" );
424   elem = xmfFile.getCheckChild( elem, "Grid" );
425 
426   xmfFile.checkAttribute( elem, "GridType", "Collection", "Expecting Collection Grid Type" );
427   xmfFile.checkAttribute( elem, "CollectionType", "Temporal", "Expecting Temporal Collection Type" );
428 
429   elem = xmfFile.getCheckChild( elem, "Grid" );
430 
431   for ( xmlNodePtr gridNod = elem;
432         gridNod != nullptr;
433         gridNod = xmfFile.getCheckSibling( gridNod, "Grid", false ) )
434   {
435     ++nTimesteps;
436     xmlNodePtr timeNod = xmfFile.getCheckChild( gridNod, "Time" );
437     RelativeTimestamp time( xmfFile.queryDoubleAttribute( timeNod, "Value" ), RelativeTimestamp::hours ); //units, supposed to be hours
438     xmlNodePtr scalarNod = xmfFile.getCheckChild( gridNod, "Attribute" );
439 
440     for ( ;
441           scalarNod != nullptr;
442           scalarNod = xmfFile.getCheckSibling( scalarNod, "Attribute", false ) )
443     {
444       xmfFile.checkAttribute( scalarNod, "Center", "Cell", "Only cell centered data is currently supported" );
445       if ( !xmfFile.checkAttribute( scalarNod, "AttributeType", "Scalar" ) &&
446            !xmfFile.checkAttribute( scalarNod, "AttributeType", "Vector" ) )
447       {
448         throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Only scalar and vector data are currently supported" );
449       }
450       std::string groupName = xmfFile.attribute( scalarNod, "Name" );
451       if ( groupName.empty() )
452       {
453         throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Group name cannot be empty" );
454       }
455 
456       xmlNodePtr itemNod = xmfFile.getCheckChild( scalarNod, "DataItem" );
457 
458       if ( xmfFile.checkAttribute( itemNod, "ItemType", "Function" ) )
459       {
460         std::string function = xmfFile.attribute( itemNod, "Function" );
461         function = MDAL::replace( function, " ", "" );
462         XdmfFunctionDataset::FunctionType type;
463         bool reversed = false;
464         bool isScalar = true;
465         if ( function == "sqrt($0/($2-$3)*$0/($2-$3)+$1/($2-$3)*$1/($2-$3))" )
466         {
467           type = XdmfFunctionDataset::Flow;
468         }
469         else if ( function == "$0-$1" )
470         {
471           reversed = true;
472           type = XdmfFunctionDataset::Subtract;
473         }
474         else if ( function == "$1-$0" )
475         {
476           type = XdmfFunctionDataset::Subtract;
477         }
478         else if ( ( function == "JOIN($0,$1,0*$1)" ) || ( function == "JOIN($0,$1,0)" ) )
479         {
480           type = XdmfFunctionDataset::Join;
481           isScalar = false;
482         }
483 
484         std::shared_ptr<MDAL::DatasetGroup> group = findGroup( groups, groupName, isScalar );
485         std::shared_ptr<MDAL::XdmfFunctionDataset> xdmfFunctionDataset = std::make_shared<MDAL::XdmfFunctionDataset>(
486               group.get(),
487               type,
488               time
489             );
490 
491         xmlNodePtr dataNod = xmfFile.getCheckChild( itemNod, "DataItem" );
492         for ( ;
493               dataNod != nullptr;
494               dataNod = xmfFile.getCheckSibling( dataNod, "DataItem", false ) )
495         {
496           if (
497             xmfFile.checkAttribute( dataNod, "ItemType", "HyperSlab" ) ||
498             xmfFile.checkAttribute( dataNod, "Type", "HyperSlab" ) )
499           {
500             std::pair<HdfDataset, HyperSlab> data = parseXdmfDataset( xmfFile, dataNod );
501             xdmfFunctionDataset->addReferenceDataset( data.second, data.first, time );
502           }
503           else
504           {
505             throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Expecting HyperSlab Types under Function" );
506           }
507         }
508 
509         if ( reversed )
510         {
511           xdmfFunctionDataset->swap();
512         }
513 
514         // This basically forces to load all data to calculate statistics!
515         const MDAL::Statistics stats = MDAL::calculateStatistics( xdmfFunctionDataset );
516         xdmfFunctionDataset->setStatistics( stats );
517         group->datasets.push_back( xdmfFunctionDataset );
518       }
519       else if (
520         xmfFile.checkAttribute( itemNod, "ItemType", "HyperSlab" ) ||
521         xmfFile.checkAttribute( itemNod, "Type", "HyperSlab" ) )
522       {
523         std::pair<HdfDataset, HyperSlab> data = parseXdmfDataset( xmfFile, itemNod );
524         std::shared_ptr<MDAL::DatasetGroup> group = findGroup( groups, groupName, data.second.isScalar );
525         std::shared_ptr<MDAL::XdmfDataset> xdmfDataset = std::make_shared<MDAL::XdmfDataset>(
526               group.get(),
527               data.second,
528               data.first,
529               time
530             );
531         // This basically forces to load all data to calculate statistics!
532         const MDAL::Statistics stats = MDAL::calculateStatistics( xdmfDataset );
533         xdmfDataset->setStatistics( stats );
534         group->datasets.push_back( xdmfDataset );
535       }
536       else
537       {
538         throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Expecting Function or HyperSlab Type" );
539       }
540     }
541   }
542 
543   // check groups
544   DatasetGroups ret;
545   for ( const auto &group : groups )
546   {
547     std::shared_ptr<MDAL::DatasetGroup> grp = group.second;
548     if ( grp->datasets.size() != nTimesteps )
549     {
550       throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Invalid group, missing timesteps" );
551     }
552 
553     const MDAL::Statistics stats = MDAL::calculateStatistics( grp );
554     grp->setStatistics( stats );
555     // verify the integrity of the dataset
556     if ( !std::isnan( stats.minimum ) )
557       ret.push_back( grp );
558   }
559 
560   return ret;
561 }
562 
findGroup(std::map<std::string,std::shared_ptr<MDAL::DatasetGroup>> & groups,const std::string & groupName,bool isScalar)563 std::shared_ptr<MDAL::DatasetGroup> MDAL::DriverXdmf::findGroup( std::map<std::string, std::shared_ptr<MDAL::DatasetGroup> > &groups,
564     const std::string &groupName,
565     bool isScalar )
566 {
567   std::shared_ptr<MDAL::DatasetGroup> group;
568   if ( groups.count( groupName ) == 0 )
569   {
570     group = std::make_shared<MDAL::DatasetGroup>(
571               "XDMF",
572               mMesh,
573               mDatFile,
574               groupName
575             );
576     group->setIsScalar( isScalar );
577     group->setDataLocation( MDAL_DataLocation::DataOnFaces ); //only center-based implemented
578     groups[groupName] = group;
579   }
580   else
581   {
582     group = groups[groupName];
583     if ( group->isScalar() != isScalar )
584     {
585       throw MDAL::Error( MDAL_Status::Err_UnknownFormat,  "Inconsistent groups" );
586     }
587   }
588   return group;
589 }
590 
591 
DriverXdmf()592 MDAL::DriverXdmf::DriverXdmf()
593   : Driver( "XDMF",
594             "XDMF",
595             "*.xdmf;;*.xmf",
596             Capability::ReadDatasets )
597 {
598 }
599 
600 MDAL::DriverXdmf::~DriverXdmf() = default;
601 
create()602 MDAL::DriverXdmf *MDAL::DriverXdmf::create()
603 {
604   return new DriverXdmf();
605 }
606 
canReadDatasets(const std::string & uri)607 bool MDAL::DriverXdmf::canReadDatasets( const std::string &uri )
608 {
609   XMLFile xmfFile;
610   try
611   {
612     xmfFile.openFile( uri );
613     xmlNodePtr root = xmfFile.getCheckRoot( "Xdmf" );
614     xmfFile.checkAttribute( root, "Version", "2.0", "Invalid version" );
615   }
616   catch ( MDAL_Status )
617   {
618     return false;
619   }
620   catch ( MDAL::Error )
621   {
622     return false;
623   }
624   return true;
625 }
626 
load(const std::string & datFile,MDAL::Mesh * mesh)627 void MDAL::DriverXdmf::load( const std::string &datFile,
628                              MDAL::Mesh *mesh )
629 {
630   assert( mesh );
631 
632   mDatFile = datFile;
633   mMesh = mesh;
634 
635   MDAL::Log::resetLastStatus();
636 
637   if ( !MDAL::fileExists( mDatFile ) )
638   {
639     MDAL::Log::error( MDAL_Status::Err_FileNotFound, name(), "File could not be found " + mDatFile );
640     return;
641   }
642 
643   try
644   {
645     // parse XML
646     DatasetGroups groups = parseXdmfXml( );
647     // add groups to mesh
648     for ( const auto &group : groups )
649     {
650       mMesh->datasetGroups.push_back( group );
651     }
652   }
653   catch ( MDAL_Status err )
654   {
655     MDAL::Log::error( err, "Error occurred while loading file " + mDatFile );
656   }
657   catch ( MDAL::Error err )
658   {
659     MDAL::Log::error( err, name() );
660   }
661 }
662