1 /*
2  MDAL - Mesh Data Abstraction Library (MIT License)
3  Copyright (C) 2019 Peter Petrik (zilolv at gmail dot com)
4 */
5 
6 #ifndef MDAL_XDMF_HPP
7 #define MDAL_XDMF_HPP
8 
9 #include <string>
10 #include <vector>
11 #include <memory>
12 #include <iosfwd>
13 #include <iostream>
14 #include <fstream>
15 #include <utility>
16 
17 #include "mdal_data_model.hpp"
18 #include "mdal.h"
19 #include "mdal_hdf5.hpp"
20 #include "mdal_driver.hpp"
21 #include "mdal_xml.hpp"
22 
23 namespace MDAL
24 {
25 
26   /**
27    * The XdmfDataset reads the data directly from HDF5 file
28    * by usage of hyperslabs retrieval
29    *
30    * http://xdmf.org/index.php/XDMF_Model_and_Format#HyperSlab
31    * HyperSlab consists of 3 rows: start, stride, and count
32    * - Currently we do not support stride other than 1 (every element)
33    * - Assumes BASEMENT 3.x format where the array is nFaces x 1
34    */
35   struct HyperSlab
36   {
37     size_t startX = 0; // offset X
38     size_t startY = 0; // offset Y
39     size_t count = 0; // number of cells/vertices
40     bool countInFirstColumn = true;
41     bool isScalar;
42   };
43 
44   /**
45    * The XdmfDataset is simple vector or scalar dataset
46    * where values are stored in one HD5 variable
47    * and are lazy loaded on demand. Active flag is always ON.
48    *
49    * The definition is stored in XML file in format:
50    *
51    * <Attribute Name="water_surface" AttributeType="Scalar" Center="Cell">
52    *   <DataItem ItemType="HyperSlab" Dimensions="9 1" Type="HyperSlab">
53    *     <DataItem Dimensions="3 2" Format="XML"> 0 0 1 1 9 1 </DataItem>
54    *     <DataItem Dimensions="9 3" Format="HDF"> test.h5:/RESULTS/CellsAll/HydState/0000002 </DataItem>
55    *   </DataItem>
56    * </Attribute>
57    */
58   class XdmfDataset: public Dataset2D
59   {
60     public:
61       XdmfDataset( DatasetGroup *grp,
62                    const HyperSlab &slab,
63                    const HdfDataset &valuesDs,
64                    MDAL::RelativeTimestamp time
65                  );
66       ~XdmfDataset() override;
67 
68       size_t scalarData( size_t indexStart, size_t count, double *buffer ) override;
69       size_t vectorData( size_t indexStart, size_t count, double *buffer ) override;
70 
71     private:
72       std::vector<hsize_t> offsets( size_t indexStart );
73       std::vector<hsize_t> selections( size_t copyValues );
74 
75       HdfDataset mHdf5DatasetValues;
76       HyperSlab mHyperSlab;
77   };
78 
79   /**
80    * The XdmfFunctionDataset is a function that
81    * references two or three scalar XdmfDatasets
82    * to create a vector or scalar dataset based on
83    * referenced datasets. Active flag is always ON.
84    *
85    * Currently we do not use any fancy bison/flex based
86    * expression parsing, just supporting few types of
87    * most common function types:
88    *   - subtraction (A-B)
89    *   - join ( [A, B] vector)
90    *   - magnitude
91    *
92    * The definition is stored in XML file in format:
93    *
94    * <Attribute Name="..." AttributeType="Scalar" Center="Cell">
95    *    <DataItem ItemType="Function" Function="$0 - $1" Dimensions="9" >
96    *     <DataItem ItemType="HyperSlab" Type="HyperSlab">...</DataItem>
97    *     <DataItem ItemType="HyperSlab" Type="HyperSlab">...</DataItem>
98    *   </DataItem>
99    * </Attribute>
100    */
101   class XdmfFunctionDataset: public Dataset2D
102   {
103     public:
104       enum FunctionType
105       {
106         Join = 1, //!< vector: [$0, $1] from 2 scalars
107         Subtract, //!< scalar: $1 - $0, e.g. calculate relative depth
108         Flow, //!< scalar: flow velocity (abs) = sqrt($0/($2-$3)*$0/($2-$3) + $1/($2-$3)*$1/($2-$3))
109       };
110 
111       XdmfFunctionDataset( DatasetGroup *grp,
112                            FunctionType type,
113                            const RelativeTimestamp &time
114                          );
115       ~XdmfFunctionDataset() override;
116 
117       //! Adds reference XMDF dataset
118       void addReferenceDataset( const HyperSlab &slab, const HdfDataset &hdfDataset, const RelativeTimestamp &time );
119       //! Swaps first and second reference dataset
120       void swap();
121 
122       size_t scalarData( size_t indexStart, size_t count, double *buffer ) override;
123       size_t vectorData( size_t indexStart, size_t count, double *buffer ) override;
124 
125     private:
126       size_t subtractFunction( size_t indexStart, size_t count, double *buffer );
127       size_t flowFunction( size_t indexStart, size_t count, double *buffer );
128       size_t joinFunction( size_t indexStart, size_t count, double *buffer );
129       size_t extractRawData( size_t indexStart, size_t count, size_t nDatasets, std::vector<double> &buf );
130 
131       const FunctionType mType;
132       std::vector<std::shared_ptr<XdmfDataset>> mReferenceDatasets;
133       /**
134        * "fake" base group for reference datasets.
135        * This group is not exposed to public API and
136        * it is just an implementation detail.
137        */
138       DatasetGroup mBaseReferenceGroup;
139   };
140 
141   class DriverXdmf: public Driver
142   {
143     public:
144       /**
145        * Driver for XDMF Files
146        *
147        * XDMF is combination of XML file with dataset metadata and
148        * HDF5 file with actual data for the datasets
149        *
150        * full file specification http://xdmf.org/index.php/XDMF_Model_and_Format
151        *
152        * XDMF file can have data (vectors) stored in different ways. Currently we
153        * only support format for BASEMENET 3.x solver
154        */
155       DriverXdmf();
156       ~DriverXdmf( ) override;
157       DriverXdmf *create() override;
158 
159       bool canReadDatasets( const std::string &uri ) override;
160       void load( const std::string &datFile, Mesh *mesh ) override;
161 
162     private:
163       /**
164        Parses XML File with this structure, where data is specified as pointers to HDF in Attribute tags
165 
166        <?xml version="1.0" ?>
167        <!DOCTYPE Xdmf SYSTEM "Xdmf.dtd" []>
168        <Xdmf Version="2.0">
169         <Domain>
170             <Topology> ... </Topology>
171             <Geometry> ... </Geometry>
172             <Grid GridType="Collection" Name="..." CollectionType="Temporal">
173                 <Grid GridType="Uniform" Name="Timestep">
174                     <Time TimeType="Single" Name="time = 0.000000" Value="0.000000"> </Time>
175                     <Topology></Topology>
176                     <Geometry GeometryType="XY" Reference="/Xdmf/Domain/Geometry[1]"></Geometry>
177                 <Attribute> ... </Attribute>
178                 </Grid>
179             </Grid>
180         <Domain>
181        </Xdmf>
182       */
183       DatasetGroups parseXdmfXml( );
184 
185       //! Finds a group with a name or creates a new group if does not exists
186       std::shared_ptr<MDAL::DatasetGroup> findGroup( std::map< std::string, std::shared_ptr<MDAL::DatasetGroup> > &groups,
187           const std::string &groupName,
188           bool isScalar );
189 
190       /**
191        * Parses scalar/vector definition for XDMF dataset, e.g.
192        *
193        *  <DataItem ItemType="HyperSlab" Dimensions="9 1" Type="HyperSlab">
194        *    <DataItem Dimensions="3 2" Format="XML"> 0 1 1 1 9 1 </DataItem>
195        *    <DataItem Dimensions="9 3" Format="HDF"> test.h5:/RESULTS/CellsAll/HydState/0000002 </DataItem>
196        *  </DataItem>
197        */
198       std::pair<HdfDataset, HyperSlab > parseXdmfDataset( const XMLFile &xmfFile, xmlNodePtr itemNod );
199 
200       //! Parses hypeslab specification from string
201       HyperSlab parseHyperSlab( const std::string &str, size_t dimB );
202 
203       /**
204        * Parses hyperslab specification from matrix in DataItem [Dimension] tag
205 
206          <DataItem ItemType="Uniform" Dimensions="3 3" Format="XML">
207            0 0 0 1 1 1 18497 1 1
208          </DataItem>
209        */
210       HyperSlab parseHyperSlabNode( const XMLFile &xmfFile, xmlNodePtr node );
211 
212       /**
213        * Parses hdf5 dataset from node
214          <DataItem Dimensions="9 3" Format="HDF"> test.h5:/RESULTS/CellsAll/HydState/0000002 </DataItem>
215        */
216       HdfDataset parseHdf5Node( const XMLFile &xmfFile, xmlNodePtr node );
217 
218       //! Extracts HDF5 filename and HDF5 Path from XML file dirname and fileName:hdfPath syntax
219       void hdf5NamePath( const std::string &dataItemPath, std::string &filePath, std::string &hdf5Path );
220 
221       //! Parses 2d matrix from text, e.g. 3 2 -> [3, 2]. Verifies that it has 2 items
222       std::vector<size_t> parseDimensions2D( const std::string &data );
223 
224       MDAL::Mesh *mMesh = nullptr;
225       std::string mDatFile;
226       std::map< std::string, std::shared_ptr<HdfFile> > mHdfFiles;
227 
228   };
229 
230 } // namespace MDAL
231 #endif //MDAL_XDMF_HPP
232