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_SELAFIN_HPP
7 #define MDAL_SELAFIN_HPP
8 
9 #include <string>
10 #include <memory>
11 #include <map>
12 #include <iostream>
13 #include <fstream>
14 
15 #include "mdal_data_model.hpp"
16 #include "mdal_memory_data_model.hpp"
17 #include "mdal.h"
18 #include "mdal_driver.hpp"
19 
20 namespace MDAL
21 {
22   /**
23    * This class is used to read the selafin file format.
24    * The file is opened with initialize() and stay opened until this object is destroyed
25    *
26    * \note SelafinFile object is shared between different datasets, with the mesh and its iterators.
27    *       As SelafinFile is not thread safe, it has to be shared in the same thread.
28    *
29    * This class can be used to create a mesh with all the dataset contained in a file with the static method createMessh()
30    * It is also pôssible to add all the dataset of a file in a separate existing mesh with the static method populateDataset()
31    *
32    * All the method to access with lazy loading to the mesh data or datasets are encapsulted and only accessible by the friend classes :
33    *    MeshSelafin
34    *    MeshSelafinVertexIterator
35    *    MeshSelafinFaceIterator
36    *    DatasetSelafin
37    *
38    * This ecapsulation protects these lazy loading access methods because they can't be used before the instance of SelafinFile has been initialized and parsed.
39    *
40   */
41   class SelafinFile
42   {
43     public:
44       //! Constructor
45       SelafinFile( const std::string &fileName );
46 
47       //! Returns a mesh created with the file
48       static std::unique_ptr<Mesh> createMesh( const std::string &fileName );
49       //! Populates the mesh with dataset from the file
50       static void populateDataset( Mesh *mesh, const std::string &fileName );
51 
52       //! Extracts data related to the mesh frame for the file
53       void parseMeshFrame();
54 
55       //! Add the dataset group to the file (persist), replace dataset in the new group by Selafindataset with lazy loading
56       bool addDatasetGroup( DatasetGroup *datasetGroup );
57 
58     private:
59 
60       //! Initializes and open the file file with the \a fileName
61       void initialize();
62 
63       //! Reads the header of the file and return the project name
64       std::string readHeader();
65 
66       //! Extracts data from the file
67       void parseFile();
68 
69       //! Returns the vertices count in the mesh stored in the file
70       size_t verticesCount();
71       //! Returns the faces count in the mesh stored in the file
72       size_t facesCount();
73       //! Returns the vertices count per face for the mesh stored in the file
74       size_t verticesPerFace();
75 
76       //! Returns \a count values at \a timeStepIndex and \a variableIndex, and an \a offset from the start
77       std::vector<double> datasetValues( size_t timeStepIndex, size_t variableIndex, size_t offset, size_t count );
78       //! Returns \a count vertex indexex in face with an \a offset from the start
79       std::vector<int> connectivityIndex( size_t offset, size_t count );
80       //! Returns \a count vertices with an \a offset from the start
81       std::vector<double> vertices( size_t offset, size_t count );
82 
83       //! Reads a string record with a size \a len from current position in the stream, throws an exception if the size in not compaitble
84       std::string readString( size_t len );
85 
86       /**
87        * Reads a double array record with a size \a len from current position in the stream,
88        * throws an exception if the size in not compatible
89        */
90       std::vector<double> readDoubleArr( size_t len );
91 
92       /**
93        * Reads a int array record with a size \a len from current position in the stream,
94        * throws an exception if the size in not compatible
95        */
96       std::vector<int> readIntArr( size_t len );
97 
98       /**
99        * Reads some values in a double array record. The values count is \a len,
100        * the reading begin at the stream \a position with the \a offset
101        */
102       std::vector<double> readDoubleArr( const std::streampos &position, size_t offset, size_t len );
103 
104       /**
105        * Reads some values in a int array record. The values count is \a len,
106        * the reading begin at the stream \a position with the \a offset
107        */
108       std::vector<int> readIntArr( const std::streampos &position, size_t offset, size_t len );
109 
110       //! Returns whether there is a int array with size \a len at the current position in the stream
111       bool checkIntArraySize( size_t len );
112 
113       //! Returns whether there is a double array with size \a len at the current position in the stream
114       bool checkDoubleArraySize( size_t len );
115 
116       //! Returns the remaining bytes in the stream from current position until the end
117       size_t remainingBytes();
118 
119       /**
120        * Set the position in the stream just after the int array with \a size, returns position of the beginning of the array
121        * The presence of int array can be check with checkIntArraySize()
122        */
123       std::streampos passThroughIntArray( size_t size );
124 
125       /**
126        * Set the position in the stream just after the double array with \a size, returns position of the beginning of the array
127        * The presence of double array can be check with checkDoubleArraySize()
128        */
129       std::streampos passThroughDoubleArray( size_t size );
130 
131       double readDouble( );
132       int readInt( );
133       size_t readSizeT( );
134 
135       void ignoreArrayLength( );
136       std::string readStringWithoutLength( size_t len );
137       void ignore( int len );
138 
139       static void populateDataset( Mesh *mesh, std::shared_ptr<SelafinFile> reader );
140 
141       // ///////
142       //  attribute updated by parseFile() method
143       // //////
144       std::vector<int> mParameters;
145       // Dataset
146       DateTime mReferenceTime;
147       std::vector<std::vector<std::streampos>> mVariableStreamPosition; //! [variableIndex][timeStep]
148       std::vector<RelativeTimestamp> mTimeSteps;
149       std::vector<std::string> mVariableNames;
150       // Mesh
151       size_t mVerticesCount;
152       size_t mFacesCount;
153       size_t mVerticesPerFace;
154       std::streampos mXStreamPosition;
155       std::streampos mYStreamPosition;
156       std::streampos mConnectivityStreamPosition;
157       std::streampos mIPOBOStreamPosition;
158       double mXOrigin;
159       double mYOrigin;
160 
161       std::string mFileName;
162       bool mStreamInFloatPrecision = true;
163       bool mChangeEndianness = true;
164       long long mFileSize = -1;
165 
166       std::ifstream mIn;
167       bool mParsed = false;
168 
169 
170       friend class MeshSelafin;
171       friend class MeshSelafinVertexIterator;
172       friend class MeshSelafinFaceIterator;
173       friend class DatasetSelafin;
174   };
175 
176   class DatasetSelafin : public Dataset2D
177   {
178     public:
179       /**
180        * Contructs a dataset with a SelafinFile object and the index of the time step
181        *
182        * \note SelafinFile object is shared between different dataset, with the mesh and its iterators.
183        *       As SerafinStreamReader is not thread safe, it has to be shared in the same thread.
184        *
185        * Position of array(s) in the stream has to be set after construction (default = begin of the stream),
186        * see setXStreamPosition() and setYStreamPosition()  (X for scalar dataset, X and Y for vector dataset)
187       */
188       DatasetSelafin( DatasetGroup *parent,
189                       std::shared_ptr<SelafinFile> reader,
190                       size_t timeStepIndex );
191 
192       size_t scalarData( size_t indexStart, size_t count, double *buffer ) override;
193       size_t vectorData( size_t indexStart, size_t count, double *buffer ) override;
194 
195       //! Sets the position of the X array in the stream
196       void setXVariableIndex( size_t index );
197       //! Sets the position of the Y array in the stream
198       void setYVariableIndex( size_t index );
199 
200     private:
201       std::shared_ptr<SelafinFile> mReader;
202 
203       size_t mXVariableIndex = 0;
204       size_t mYVariableIndex = 0;
205       size_t mTimeStepIndex = 0;
206   };
207 
208   class MeshSelafinVertexIterator: public MeshVertexIterator
209   {
210     public:
211       /**
212        * Contructs a vertex iterator with a SerafinFile instance
213        *
214        * \note SerafinFile instance is shared between different dataset, with the mesh and its iterators.
215        *       As SerafinStreamReader is not thread safe, it has to be shared in the same thread.
216        */
217       MeshSelafinVertexIterator( std::shared_ptr<SelafinFile> reader );
218 
219       size_t next( size_t vertexCount, double *coordinates ) override;
220 
221     private:
222       std::shared_ptr<SelafinFile> mReader;
223       size_t mPosition = 0;
224   };
225 
226   class MeshSelafinFaceIterator: public MeshFaceIterator
227   {
228     public:
229       /**
230        * Contructs a face iterator with a SerafinFile instance
231        *
232        * \note SerafinFile instance is shared between different dataset, with the mesh and its iterators.
233        *       As SerafinFile is not thread safe, it has to be shared in the same thread.
234        */
235       MeshSelafinFaceIterator( std::shared_ptr<SelafinFile> reader );
236 
237       size_t next( size_t faceOffsetsBufferLen, int *faceOffsetsBuffer, size_t vertexIndicesBufferLen, int *vertexIndicesBuffer ) override;
238 
239     private:
240       std::shared_ptr<SelafinFile> mReader;
241       size_t mPosition = 0;
242   };
243 
244   class MeshSelafin: public Mesh
245   {
246     public:
247       /**
248        * Contructs a dataset with a SerafinFile instance \a reader
249        *
250        * \note SerafinFile instance is shared between different dataset, with the mesh and its iterators.
251        *       As SerafinFile is not thread safe, it has to be shared in the same thread.
252       */
253       MeshSelafin( const std::string &uri,
254                    std::shared_ptr<SelafinFile> reader );
255 
256       std::unique_ptr<MeshVertexIterator> readVertices() override;
257 
258       //! Selafin format doesn't support edges in MDAL, returns a void unique_ptr
259       std::unique_ptr<MeshEdgeIterator> readEdges() override;
260 
261       std::unique_ptr<MeshFaceIterator> readFaces() override;
262 
verticesCount() const263       size_t verticesCount() const override {return mReader->verticesCount();}
edgesCount() const264       size_t edgesCount() const override {return 0;}
facesCount() const265       size_t facesCount() const override {return mReader->facesCount();}
266       BBox extent() const override;
267 
268       void closeSource() override;
269 
270     private:
271       mutable bool mIsExtentUpToDate = false;
272       mutable BBox mExtent;
273 
274       std::shared_ptr<SelafinFile> mReader;
275 
276       void calculateExtent() const;
277   };
278 
279   /**
280    * Serafin format (also called Selafin)
281    *
282    * Binary format for triangular mesh with datasets defined on vertices
283    * Source of this doc come from :
284    * http://www.opentelemac.org/downloads/MANUALS/TELEMAC-2D/telemac-2d_user_manual_en_v7p0.pdf Appendix 3
285    * https://www.gdal.org/drv_selafin.html
286    *
287    * The Selafin file records are listed below:
288    * - 1 record containing the title of the study (72 characters) and a 8 characters string indicating the type
289    *    of format (SERAFIN or SERAFIND)
290    * - record containing the two integers NBV(1)and NBV(2)(number of linear and quadratic variables, NBV(2)with the value of 0 for Telemac,
291    * cas quadratic values are not saved so far),
292    * - NBV(1)records containing the names and units of each variable (over 32 characters),
293    * - 1 record containing the integers table IPARAM(10 integers, of which only the 6are currently being used),
294    *        - if IPARAM (3)!=0: the value corresponds to the x-coordinate of the origin of the mesh,
295    *        - if IPARAM (4)!=0: the value corresponds to the y-coordinate of the origin of the mesh,
296    *        - if IPARAM (7): the value corresponds to the number of  planes on the vertical (3D computation),
297    *        - if IPARAM (8)!=0: the value corresponds to the number of boundary points (in parallel),
298    *        - if IPARAM (9)!=0: the value corresponds to the number of interface points (in parallel),
299    *        - if IPARAM(8 ) or IPARAM(9) !=0: the array IPOBO below is replaced by the array KNOLG(total initial number of points).
300    *            All the other numbers are local to the sub-domain, including IKLE.
301    *
302    * - if IPARAM(10)= 1: a record containing the computation starting date,
303    * - 1 record containing the integers NELEM,NPOIN,NDP,1(number of elements, number of points, number of points per element and the value 1),
304    * - 1 record containing table IKLE(integer array of dimension (NDP,NELEM) which is the connectivity table.
305    *    N.B.: in TELEMAC-2D, the dimensions of this array are (NELEM,NDP)),
306    * - 1 record containing table IPOBO(integer array of dimension NPOIN);
307    *    the value of one element is 0 for an internal point, and gives the numbering of boundary points for the others,
308    * - 1 record containing table X(real array of dimension NPOINcontaining the abscissae of the points),
309    * - 1 record containing table Y(real array of dimension NPOINcontaining the ordinates of the points),
310    *
311    * Next, for each time step, the following are found:
312    * - 1 record containing time T(real),
313    * - NBV(1)+NBV(2)records containing the results tables for each variable at time T.
314    */
315   class DriverSelafin: public Driver
316   {
317     public:
318       DriverSelafin();
319       ~DriverSelafin() override;
320       DriverSelafin *create() override;
321 
322       bool canReadMesh( const std::string &uri ) override;
323       bool canReadDatasets( const std::string &uri ) override;
324 
325       std::unique_ptr< Mesh > load( const std::string &meshFile, const std::string &meshName = "" ) override;
326       void load( const std::string &datFile, Mesh *mesh ) override;
327 
328       bool persist( DatasetGroup *group ) override;
329 
faceVerticesMaximumCount() const330       int faceVerticesMaximumCount() const override {return 3;}
331       void save( const std::string &fileName,  const std::string &meshName, Mesh *mesh ) override;
332 
333       std::string writeDatasetOnFileSuffix() const override;
334       std::string saveMeshOnFileSuffix() const override;
335 
336     private:
337       bool saveDatasetGroupOnFile( DatasetGroup *datasetGroup );
338   };
339 
340 } // namespace MDAL
341 #endif //MDAL_SELAFIN_HPP
342