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