Initial public release.
[OpenCLIPER] / src / ConcreteNDArray.cpp
1 /* Copyright (C) 2018 Federico Simmross Wattenberg,
2  *                    Manuel Rodríguez Cayetano,
3  *                    Javier Royuela del Val,
4  *                    Elena Martín González,
5  *                    Elisa Moya Sáez,
6  *                    Marcos Martín Fernández and
7  *                    Carlos Alberola López
8  *
9  * This file is part of OpenCLIPER.
10  *
11  * OpenCLIPER is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; version 3 of the License.
14  *
15  * OpenCLIPER is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with OpenCLIPER; If not, see <http://www.gnu.org/licenses/>.
22  *
23  *
24  *  Contact:
25  *
26  *  Federico Simmross Wattenberg
27  *  E.T.S.I. Telecomunicación
28  *  Universidad de Valladolid
29  *  Paseo de Belén 15
30  *  47011 Valladolid, Spain.
31  *  fedsim@tel.uva.es
32  */
33 /*
34  * ConcreteNDArray.cpp
35  *
36  *  Created on: 26 de oct. de 2016
37  *      Author: manrod
38  */
39 #include<fstream>
40 #include<OpenCLIPER/ConcreteNDArray.hpp>
41 #include<typeinfo>
42
43 namespace OpenCLIPER {
44
45 /** 
46  * @brief Constructor without parameters, initializes class fields. It calls superclass default constructor.
47  */
48 template <class T>
49 ConcreteNDArray<T>::ConcreteNDArray(): NDArray() {
50 }
51
52 /**
53  * @brief Constructor for storing spatial dimensions and empty data in class fields (element data type is complexType).
54  * 
55  * This constructor has move semantics (in spite of not using && notation):
56  * after call, parameters memory deallocation is responsibility of this class
57  * (parameters are set to nullptr at the end of the method).
58  * @param[in,out] pSpatialDims vector with sizes of each spatial dimension
59  */
60 template <>
61 ConcreteNDArray<complexType>::ConcreteNDArray(std::vector<dimIndexType>*& pSpatialDims) {
62     setDims(pSpatialDims);
63     complexType complexZero(0.0,0.0);
64     std::vector <complexType>* pHostComplexDataLocal = new std::vector <complexType>(this->size(), complexZero);
65     setHostData(pHostComplexDataLocal);
66 }
67
68 /**
69  * @brief Constructor for storing spatial dimensions and empty data in class fields (element data type is dimIndexType).
70  * 
71  * This constructor has move semantics (in spite of not using && notation):
72  * after call, parameters memory deallocation is responsibility of this class
73  * (parameters are set to nullptr at the end of the method).
74  * @param[in,out] pSpatialDims vector with sizes of each spatial dimension
75  */
76 template <>
77 ConcreteNDArray<dimIndexType>::ConcreteNDArray(std::vector<dimIndexType>*& pSpatialDims) {
78     setDims(pSpatialDims);
79     std::vector <dimIndexType>* pHostUnsignedDataLocal = new std::vector <dimIndexType>(this->size(), 0);
80     setHostData(pHostUnsignedDataLocal);
81 }
82
83 /**
84  * @brief Constructor for storing spatial dimensions and empty data in class fields (element data type is realType).
85  * 
86  * This constructor has move semantics (in spite of not using && notation):
87  * after call, parameters memory deallocation is responsibility of this class
88  * (parameters are set to nullptr at the end of the method).
89  * @param[in,out] pSpatialDims vector with sizes of each spatial dimension
90  */
91 template <>
92 ConcreteNDArray<realType>::ConcreteNDArray(std::vector<dimIndexType>*& pSpatialDims) {
93     setDims(pSpatialDims);
94     std::vector <realType>* pHostLocal = new std::vector <realType>(this->size(), 0.0);
95     setHostData(pHostLocal);
96 }
97
98 /**
99  * @brief Constructor for storing spatial dimensions and data in class fields.
100  * This constructor has move semantics (in spite of not using && notation):
101  * after call, parameters memory deallocation is responsibility of this class
102  * (parameters are set to nullptr at the end of the method).
103  * @param[in,out] pSpatialDims vector with sizes of each spatial dimension
104  * @param[in,out] pHostData vector of \<T\> type data elements stored in host memory
105  */
106 template <class T>
107 ConcreteNDArray<T>::ConcreteNDArray(std::vector<dimIndexType>*& pSpatialDims, std::vector<T>*&pHostData) {
108     setDims(pSpatialDims);
109     setHostData(pHostData);
110 }
111
112 /**
113  * @brief Constructor that reads data for the ConcreteNDArray object from a file in raw format.
114  * @param[in] completeFileName name of the file to be read for getting data
115  * @param[in,out] pSpatialDims vector with NDArray spatial dimensions (move semantics, ownership of the vector is transferred from caller to this object)
116  */
117 template <class T>
118 ConcreteNDArray<T>::ConcreteNDArray(const string completeFileName, vector<dimIndexType>*& pSpatialDims) {
119     vector<T>* pTempData;
120     setDims(pSpatialDims); // store dimension vector and set parameter to nullptr (move semantics)
121     pTempData = new vector<T>; // vector for data
122     pTempData->resize(this->size()); //size(): product of size of each dimension
123     std::ifstream f(completeFileName, std::ios::binary);
124     if (!f.good()) {
125         throw std::invalid_argument(completeFileName + " cannot be read\n");
126     }
127     // get length of file:
128     f.seekg (0, f.end);
129     int fileLength = f.tellg();
130     f.seekg (0, f.beg);
131     //CERR (completeFileName << " file length: " << fileLength << " bytes\n");
132     f.read(reinterpret_cast<char*>(pTempData->data()), this->size() * sizeof(T));
133     // store data and set parameter to nullptr (move semantics)
134     setHostData(pTempData);
135     f.close();
136 }
137
138 /**
139  * @brief Constructor that creates a copy of a ConcreteNDArray object  with complexType data type elements (dimensions are copied always, 
140  * image data only if copyData parameter is true).
141  * @param[in] pSourceData ConcreteNDArray object source of spatial and temporal dimensions (complexType elements)
142  * @param[in] copyData data (not only dimensions) are copied if this parameter is true (default value: false)
143  */
144 template <>
145 ConcreteNDArray<complexType>::ConcreteNDArray(const NDArray* pSourceData, bool copyData) {
146     vector<dimIndexType>* pLocalDims = new vector<dimIndexType>(*(pSourceData->getDims()));
147     setDims(pLocalDims);
148     vector<complexType>* pLocalHostData;
149     if (copyData) {
150         const ConcreteNDArray<complexType>* pTypedSourceData = static_cast<const ConcreteNDArray<complexType>*>(pSourceData);
151         pLocalHostData = new vector<complexType>(*(pTypedSourceData->getHostData()));
152     } else {
153         // Create image data initialized to a vector of complex values (0.0, 0.0) and with a number of values equal to the
154         // multiplication of dims vector values
155         complexType zeroElement(0.0, 0.0);
156         pLocalHostData = new std::vector<complexType>(pSourceData->size(), zeroElement);
157     }
158     setHostData(pLocalHostData);
159 }
160
161 /**
162  * @brief Constructor that creates a copy of a ConcreteNDArray object with dimIndexType data type elements (dimensions are copied always, 
163  * image data only if copyData parameter is true).
164  * @param[in] pSourceData ConcreteNDArray object source of spatial and temporal dimensions (dimIndexType elements)
165  * @param[in] copyData data (not only dimensions) are copied if this parameter is true (default value: false)
166  */
167 template <>
168 ConcreteNDArray<dimIndexType>::ConcreteNDArray(const NDArray* pSourceData, bool copyData) {
169     vector<dimIndexType>* pLocalDims = new vector<dimIndexType>(*(pSourceData->getDims()));
170     setDims(pLocalDims);
171     vector<dimIndexType>* pLocalHostData;
172     if (copyData) {
173         const ConcreteNDArray<dimIndexType>* pTypedSourceData = static_cast<const ConcreteNDArray<dimIndexType>*>(pSourceData);
174         pLocalHostData = new vector<dimIndexType>(*(pTypedSourceData->getHostData()));
175     } else {
176         dimIndexType zeroElement = 0;
177         pLocalHostData = new std::vector<dimIndexType>(pSourceData->size(), zeroElement);
178     }
179     setHostData(pLocalHostData);
180 }
181
182 /**
183  * @brief Constructor that creates a copy of a ConcreteNDArray object with realType data type elements (dimensions are copied always, 
184  * image data only if copyData parameter is true).
185  * @param[in] pSourceData ConcreteNDArray object source of spatial and temporal dimensions (realType elements)
186  * @param[in] copyData data (not only dimensions) are copied if this parameter is true (default value: false)
187  */
188 template <>
189 ConcreteNDArray<realType>::ConcreteNDArray(const NDArray* pSourceData, bool copyData) {
190     vector<dimIndexType>* pLocalDims = new vector<dimIndexType>(*(pSourceData->getDims()));
191     setDims(pLocalDims);
192     vector<realType>* pLocalHostData;
193     if (copyData) {
194         const ConcreteNDArray<realType>* pTypedSourceData = static_cast<const ConcreteNDArray<realType>*>(pSourceData);
195         pLocalHostData = new vector<realType>(*(pTypedSourceData->getHostData()));
196     } else {
197         realType zeroElement = 0.0;
198         pLocalHostData = new std::vector<realType>(pSourceData->size(), zeroElement);
199     }
200     setHostData(pLocalHostData);
201 }
202
203 /**
204  * @brief Constructor for reading data from a matlab variable
205  * @param[in] matvar matlab array variable read from file
206  * @param[in] numOfSpatialDims number of dimensions of matlab array variable that are used as data spatial dimensions
207  * @param[in] nDArrayOffsetInElements  offset (in number of elements) from matlab variable beginning to start reading from
208  */
209 template <class T>
210 ConcreteNDArray<T>::ConcreteNDArray(matvar_t *matvar, dimIndexType numOfSpatialDims, dimIndexType nDArrayOffsetInElements) {
211     loadMatlabHostData(matvar, numOfSpatialDims, nDArrayOffsetInElements);
212 #ifdef ConcreteNDArray_DEBUG
213     CERR(hostDataToString("ConcreteNDArray: "));
214 #endif
215 }
216
217 /**
218  * @brief Gets one element from a matlab variable (previously read from a matlab file), base data type is complexType
219  * @param[in] matvar matlab array variable previously read from file
220  * @param[in] offsetInBytes offset from beginning of matlab variable (in bytes) where element data must be read
221  */
222 template <> 
223 inline void ConcreteNDArray<complexType>::loadMatlabHostDataElement(matvar_t* matvar, dimIndexType offsetInBytes) {
224     mat_complex_split_t *complex_data = (mat_complex_split_t *) matvar->data;
225     char* pCharRealPart = (char *) complex_data->Re;
226     char* pCharImagPart = (char *) complex_data->Im;
227     complexType complexElement;
228     realType realPart, imagPart;
229     realPart = *((realType*)(pCharRealPart + offsetInBytes));
230     imagPart = *((realType*)(pCharImagPart + offsetInBytes));
231     complexElement = {realPart, imagPart};
232     pHostData->push_back(complexElement);
233 #ifdef ConcreteNDArray_DEBUG
234     CERR(realPart << "+" << imagPart << "i" << std::endl);
235 #endif
236 }
237
238 /**
239  * @brief Gets one element from a matlab variable (previously read from a matlab file), base data type is dimIndexType
240  * @param[in] matvar matlab array variable previously read from file
241  * @param[in] offsetInBytes offset from beginning of matlab variable (in bytes) where element data must be read
242  */
243 template <> 
244 inline void ConcreteNDArray<dimIndexType>::loadMatlabHostDataElement(matvar_t* matvar, dimIndexType offsetInBytes) {
245     char *data = (char*)matvar->data;
246     dimIndexType element;
247     element = *((dimIndexType *)(data + offsetInBytes));
248     pHostData->push_back(element);
249 #ifdef ConcreteNDArray_DEBUG
250     CERR(element << std::endl);
251 #endif
252 }
253
254 /**
255  * @brief Gets one element from a matlab variable (previously read from a matlab file), base data type is realType
256  * @param[in] matvar matlab array variable previously read from file
257  * @param[in] offsetInBytes offset from beginning of matlab variable (in bytes) where element data must be read
258  */
259 template <> 
260 inline void ConcreteNDArray<realType>::loadMatlabHostDataElement(matvar_t* matvar, dimIndexType offsetInBytes) {
261     char *data = (char*)matvar->data;
262     realType element;
263     element = *((realType *)(data + offsetInBytes));
264     pHostData->push_back(element);
265 #ifdef ConcreteNDArray_DEBUG
266     CERR(element << std::endl);
267 #endif
268 }
269
270 /**
271  * @brief Destructor, frees all previously allocated memory
272  */
273 template <class T>
274 ConcreteNDArray<T>::~ConcreteNDArray() {
275 #ifdef ConcreteNDArray_DEBUG
276     CERR("~ConcreteNDArray() begins..." << std::endl);
277 #endif
278     if (pHostData != nullptr) {
279         // If host2device have not been called, pHostBuffer and pHostImage point to pHostData->data, 
280         // and they must be set to nullptr (to avoid accesses to deallocated memory after pHostData has been reset)
281         if (pHostBuffer == pHostData->data()) {
282             pHostBuffer = nullptr;
283         }
284         if (pHostImage == pHostData->data()) {
285             pHostImage = nullptr;
286         }
287         pHostData.reset(); // pHostData is a smart pointer
288     }
289 #ifdef ConcreteNDArray_DEBUG
290     CERR("~ConcreteNDArray() ends" << std::endl);
291 #endif
292 }
293
294 /**
295  * @brief Converts one data element of an NDArray to a text representation
296  * @param[in] pElementsArray pointer to array of elements
297  * @param[in] index1D 1-dimensional index for element from array
298  * @return string with text representation of data element
299  */
300 template <class T> const std::string ConcreteNDArray<T>::elementToString(const void *pElementsArray, dimIndexType index1D) const {
301     string stringValue;
302     if (typeid(T) == typeid(complexType)) {
303         complexType* pTypedArray;
304         pTypedArray = (complexType *) pElementsArray;
305         stringValue = "(" + std::to_string(pTypedArray[index1D].real()) + "," +
306                             std::to_string(pTypedArray[index1D].imag()) + ")";
307     } else if (typeid(T) == typeid(dimIndexType)) {
308         dimIndexType* pTypedArray;
309         pTypedArray = (dimIndexType*) pElementsArray;
310         stringValue = std::to_string(pTypedArray[index1D]);
311     } else if (typeid(T) == typeid(realType)) {
312         realType* pTypedArray;
313         pTypedArray = (realType*) pElementsArray;
314         stringValue = std::to_string(pTypedArray[index1D]);
315     } else {
316         throw invalid_argument("element data type not supported in elementToString method: " + string(typeid(T).name()));
317     }
318     return stringValue;    
319 }
320
321 /**
322  * @brief Calculates rows, columns, slices, ... offsets for accessing images/volumes stored as 1D arrays of \<T\> type 
323  * elements
324  * @return pointer to vector variable storing strides
325  */
326 template <class T>
327 vector <dimIndexType>* ConcreteNDArray<T>::calcUnaligned1DArrayStridesFromNDArrayDims() const {
328     vector<dimIndexType>* pSpatialDimsStridesVector = new vector<dimIndexType>();
329     // dimensions order is columns, rows, slices, ...
330     dimIndexType acumStride;
331     if (typeid(T) == typeid(complexType)) {
332         acumStride = 2;// every column has 2 floats (real and imaginary part of complex number)
333     } else if (typeid(T) == typeid(realType)) {
334         acumStride = 1;// every column has 1 float (instead of 2 floats, real and imaginary part of complex number, for a ComplexNDArray)
335     } else if (typeid(T) == typeid(dimIndexType)) {
336         acumStride = 1;// every column has 1 uint (instead of 2 floats, real and imaginary part of complex number, for a ComplexNDArray)
337     } else {
338         throw invalid_argument("Unsupported type in calcUnaligned1DArrayStridesFromNDArrayDims method: " + string(typeid(this).name()));
339     }
340     for (dimIndexType spatialDimId = 0; spatialDimId < getNDims(); spatialDimId++) {
341         pSpatialDimsStridesVector->push_back(acumStride);
342         acumStride *= getDims()->at(spatialDimId);  // new stride is equal to previous stride * previous dimension
343     }
344     return pSpatialDimsStridesVector;
345 }
346
347 // Doxygen comments not needed, inherited from superclass method of the same name
348 template <>
349 inline void ConcreteNDArray<complexType>::dumpMatlabElement(MatVarInfo* pMatVarInfo, dimIndexType matlabElementOffset, 
350                                                             dimIndexType nDArrayElementOffset, SyncSource syncSource) {
351     mat_complex_split_t* complexData = (mat_complex_split_t*) pMatVarInfo->getData();
352     // WARNING!! pointer arithmetic depends on base type (pointer + offset operation increments pointer in a number of bytes equal 
353     // to offset multiplied by the base data type size in bytes, you must not explicity multiply offset by base data type size!!)
354     realType* realPart = ((realType*) complexData->Re) + matlabElementOffset;
355     realType* imagPart = ((realType*) complexData->Im) + matlabElementOffset;
356     realType* source; // type of base element (real, not complexType)
357     switch (syncSource) {
358         case SyncSource::BUFFER_ONLY:
359             source = (realType*)(getHostBuffer());
360             break;
361         case SyncSource::IMAGE_ONLY:
362             source = (realType*)(getHostImage());
363             break;
364         default:
365             throw invalid_argument("Unsupported SyncSource in dumpMatlabElement");
366     };
367     // real and imaginary parts in OpenCL buffer/images stored in contiguous positions (nDArrayElementOffset: real part, 
368     // nDArrayElementOffset + 1: imaginary part
369     *realPart = (source)[nDArrayElementOffset];
370     *imagPart = (source)[nDArrayElementOffset+1];
371 }
372
373 template <>
374 inline void ConcreteNDArray<dimIndexType>::dumpMatlabElement(MatVarInfo* pMatVarInfo, dimIndexType matlabElementOffset, 
375                                                              dimIndexType nDArrayElementOffset, SyncSource syncSource) {
376     dimIndexType* typedData = ((dimIndexType*) pMatVarInfo->getData()) + matlabElementOffset;
377     dimIndexType* source; 
378     switch (syncSource) {
379         case SyncSource::BUFFER_ONLY:
380             source = (dimIndexType*)(getHostBuffer());
381             break;
382         case SyncSource::IMAGE_ONLY:
383             source = (dimIndexType*)(getHostImage());
384             break;
385         default:
386             throw invalid_argument("Unsupported SyncSource in dumpMatlabElement");
387     };
388     *typedData = (source)[nDArrayElementOffset];
389 }
390
391 template <>
392 inline void ConcreteNDArray<realType>::dumpMatlabElement(MatVarInfo* pMatVarInfo, dimIndexType matlabElementOffset, 
393                                                          dimIndexType nDArrayElementOffset, SyncSource syncSource) {
394     realType* typedData = ((realType*) pMatVarInfo->getData()) + matlabElementOffset;
395     realType* source; 
396     switch (syncSource) {
397         case SyncSource::BUFFER_ONLY:
398             source = (realType*)(getHostBuffer());
399             break;
400         case SyncSource::IMAGE_ONLY:
401             source = (realType*)(getHostImage());
402             break;
403         default:
404             throw invalid_argument("Unsupported SyncSource in dumpMatlabElement");
405     };
406     *typedData = (source)[nDArrayElementOffset];
407 }
408
409 #include<OpenCLIPER/ConcreteNDArrayPrototypes.hpp>
410 } //namespace OpenCLIPER