svFSIplus
Array3.h
1 /**
2  * Copyright (c) Stanford University, The Regents of the University of California, and others.
3  *
4  * All Rights Reserved.
5  *
6  * See Copyright-SimVascular.txt for additional details.
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining
9  * a copy of this software and associated documentation files (the
10  * "Software"), to deal in the Software without restriction, including
11  * without limitation the rights to use, copy, modify, merge, publish,
12  * distribute, sublicense, and/or sell copies of the Software, and to
13  * permit persons to whom the Software is furnished to do so, subject
14  * to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included
17  * in all copies or substantial portions of the Software.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
23  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include "Array.h"
33 
34 #ifndef ARRAY3_H
35 #define ARRAY3_H
36 
37 #include <float.h>
38 #include <iostream>
39 #include <string>
40 #include <cstring>
41 
42 //#define Array3_check_enabled
43 
44 template<typename T>
45 
46 /*! @brief The Array3 template class implements a simple interface to 3D arrays.
47 *
48 * A 3D array is defined by (num_rows * num_cols) * num_slices values.
49 */
50 class Array3
51 {
52  public:
53 
54  static int num_allocated;
55  static int active;
56  static double memory_in_use;
57  static double memory_returned;
58  static bool write_enabled;
59  static void memory(const std::string& prefix="");
60  static void stats(const std::string& prefix="");
61 
62  Array3()
63  {
64  nrows_ = 0;
65  ncols_ = 0;
66  nslices_ = 0;
67  slice_size_ = 0;
68  size_ = 0;
69  data_ = nullptr;
70  num_allocated += 1;
71  active += 1;
72  };
73 
74  Array3(const int num_rows, const int num_cols, const int num_slices, bool row_major=true)
75  {
76  // [NOTE] This is tfu but need to mimic fortran.
77  nrows_ = num_rows;
78  ncols_ = num_cols;
79  nslices_ = num_slices;
80 
81  if ((num_rows <= 0) || (num_cols <= 0) || (num_slices <= 0)) {
82  return;
83  //throw std::runtime_error(+"Allocating zero size Array3.");
84  }
85 
86  allocate(num_rows, num_cols, num_slices, row_major);
87  num_allocated += 1;
88  active += 1;
89  }
90 
91  /// @brief Array copy
92  Array3(const Array3 &rhs)
93  {
94  if ((rhs.nrows_ <= 0) || (rhs.ncols_ <= 0) || (rhs.nslices_ <= 0)) {
95  return;
96  }
97 
98  allocate(rhs.nrows_, rhs.ncols_, rhs.nslices_);
99  memcpy(data_, rhs.data_, size_*sizeof(T));
100  num_allocated += 1;
101  active += 1;
102  }
103 
104  ~Array3()
105  {
106  if (data_ != nullptr) {
107  memory_in_use -= sizeof(T) * size_;;
108  memory_returned += sizeof(T) * size_;;
109  active -= 1;
110  delete [] data_;
111  data_ = nullptr;
112  }
113  }
114 
115  int ncols() const { return ncols_; }
116  int nrows() const { return nrows_; }
117  int nslices() const { return nslices_; }
118 
119  void allocate(const int num_rows, const int num_cols, const int num_slices, const bool row_major=true)
120  {
121  nrows_ = num_rows;
122  ncols_ = num_cols;
123  nslices_ = num_slices;
124  slice_size_ = ncols_ * nrows_;
125  size_ = nrows_ * ncols_ * nslices_;
126  data_ = new T [size_];
127  memset(data_, 0, sizeof(T)*size_);
128  memory_in_use += sizeof(T) * size_;;
129  }
130 
131  void check_index(const int i, const int j, const int k) const
132  {
133  if (data_ == nullptr) {
134  throw std::runtime_error(+"Accessing null data in Array3.");
135  }
136 
137  if ((i < 0) || (i >= nrows_) or (j < 0) || (j >= ncols_) or (k < 0) || (k >= nslices_)) {
138  auto i_str = std::to_string(nrows_);
139  auto j_str = std::to_string(ncols_);
140  auto k_str = std::to_string(nslices_);
141  auto dims = i_str + " x " + j_str + " x " + k_str;
142  auto index_str = " " + std::to_string(i) + "," + std::to_string(j) + "," + std::to_string(k) + " ";
143  throw std::runtime_error("Index (i,j,k)=" + index_str + " is out of bounds for " + dims + " array.");
144  }
145  }
146 
147  friend std::ostream& operator << (std::ostream& out, const Array3<T>& lhs)
148  {
149  if (lhs.data_ == nullptr) {
150  throw std::runtime_error("[Array3] Accessing null data in ostream.");
151  }
152 
153  for (int i = 0; i < lhs.size(); i++) {
154  out << lhs.data_[i];
155  if (i != lhs.size()-1) {
156  out << ", ";
157  }
158  }
159  return out;
160  }
161 
162  /// @brief Free the array data
163  ///
164  /// This is to replicate the Fortran DEALLOCATE().
165  void clear()
166  {
167  if (data_ != nullptr) {
168  delete [] data_;
169  memory_in_use -= sizeof(T) * size_;;
170  memory_returned += sizeof(T) * size_;;
171  }
172 
173  nrows_ = 0;
174  ncols_ = 0;
175  nslices_ = 0;
176  slice_size_ = 0;
177  size_ = 0;
178  data_ = nullptr;
179  }
180 
181  /// @brief rslice
182  ///
183  /// Return an Array with data pointing into the Array3 internal data.
184  Array<T> rslice(const int slice) const
185  {
186  #ifdef Array3_check_enabled
187  check_index(0, 0, slice);
188  #endif
189 
190  Array<T> array_slice(nrows_, ncols_, &data_[slice*slice_size_]);
191 
192  return array_slice;
193  }
194 
195  T* slice_data(const int slice) {
196  return &data_[slice*slice_size_];
197  }
198 
199  void print(const std::string& label)
200  {
201  printf("%s (%d): \n", label.c_str(), size_);
202  for (int i = 0; i < size_; i++) {
203  if (data_[i] != 0.0) {
204  printf("%s %d %g\n", label.c_str(), i+1, data_[i]);
205  }
206  }
207  }
208 
209  /// @brief Get a slice.
210  Array<T> slice(const int slice) const
211  {
212  #ifdef Array3_check_enabled
213  check_index(0, 0, slice);
214  #endif
215  Array<T> array_slice(nrows_, ncols_);
216 
217  for (int col = 0; col < ncols_; col++) {
218  for (int row = 0; row < nrows_; row++) {
219  array_slice(row, col) = data_[row + col*nrows_ + slice*slice_size_];
220  }
221  }
222 
223  return array_slice;
224  }
225 
226  void set_row(const int col, const int slice, const std::vector<T>& values) const
227  {
228  #ifdef Array3_check_enabled
229  check_index(0, col, slice);
230  #endif
231  for (int row = 0; row < values.size(); row++) {
232  data_[row + col*nrows_ + slice*slice_size_] = values[row];
233  }
234  }
235 
236  void set_slice(const int slice, const Array<T>& values) const
237  {
238  #ifdef Array3_check_enabled
239  check_index(0, 0, slice);
240  #endif
241  for (int col = 0; col < ncols_; col++) {
242  for (int row = 0; row < nrows_; row++) {
243  data_[row + col*nrows_ + slice*slice_size_] = values(row,col);
244  }
245  }
246  }
247 
248  int size() const
249  {
250  return size_;
251  }
252 
253  /// @brief Test if an array has rows or columns or slices set but no data.
254  ///
255  /// [NOTE] This is tfu but need to mimic fortran.
256  bool allocated()
257  {
258  if ((nrows_ > 0) || (ncols_ > 0) || nslices_ > 0) {
259  return true;
260  }
261 
262  return false;
263  }
264 
265  int msize() const
266  {
267  return size_ * sizeof(T);
268  }
269 
270  /// @brief Resize the array.
271  void resize(const int num_rows, const int num_cols, const int num_slices)
272  {
273  // [NOTE] This is tfu but need to mimic fortran.
274  nrows_ = num_rows;
275  ncols_ = num_cols;
276  nslices_ = num_slices;
277 
278  if ((num_rows <= 0) || (num_cols <= 0) || (num_slices <= 0)) {
279  return;
280  }
281 
282  if (data_ != nullptr) {
283  delete [] data_;
284  memory_in_use -= sizeof(T) * size_;;
285  memory_returned += sizeof(T) * size_;;
286  data_ = nullptr;
287  }
288 
289  allocate(num_rows, num_cols, num_slices);
290  }
291 
292  /// @brief Set the array values from an Array with the equivalent number of values.
293  ///
294  /// This sort of replicates the Fortan reshape function.
295  void set_values(Array<T>& rhs)
296  {
297  int rhs_size = rhs.size();
298 
299  if (size_ != rhs_size) {
300  throw std::runtime_error("The RHS size " + std::to_string(rhs_size) + " does not have the same size " +
301  std::to_string(size_) + " of this array.");
302  }
303 
304  auto rhs_data = rhs.data();
305 
306  for (int i = 0; i < size_; i++) {
307  data_[i] = rhs_data[i];
308  }
309  }
310 
311  void read(const std::string& file_name)
312  {
313  auto fp = fopen(file_name.c_str(), "rb");
314  fread(&size_, sizeof(int), 1, fp);
315  fread(data_, sizeof(double), size_, fp);
316  fclose(fp);
317  }
318 
319  void write(const std::string& label, bool memory=true)
320  {
321  if (!write_enabled) {
322  return;
323  }
324 
325  auto file_prefix = build_file_prefix(label);
326  auto file_name = file_prefix + "_cm.bin";
327 
328  // Write binary file.
329  auto fp = fopen(file_name.c_str(), "wb");
330  fwrite(&size_, sizeof(int), 1, fp);
331  fwrite(data_, sizeof(double), size_, fp);
332  fclose(fp);
333  }
334 
335  void append(const std::string& label, bool memory=true)
336  {
337  if (!write_enabled) {
338  return;
339  }
340 
341  auto file_prefix = build_file_prefix(label);
342  auto file_name = file_prefix + "_cm.bin";
343 
344  // Append to binary file.
345  auto fp = fopen(file_name.c_str(), "ab");
346  fwrite(data_, sizeof(double), size_, fp);
347  fclose(fp);
348  }
349 
350  /////////////////////////
351  // O p e r a t o r s //
352  /////////////////////////
353 
354  /// @brief Array assignment
355  ///
356  /// Copy data to replicate Fortran behavior.
357  Array3& operator = (const Array3& rhs)
358  {
359  if ((rhs.nrows_ <= 0) || (rhs.ncols_ <= 0) || (rhs.nslices_ <= 0)) {
360  return *this;
361  }
362 
363  if (rhs.data_ == nullptr) {
364  throw std::runtime_error(+"RHS has null data.");
365  }
366 
367  if (this == &rhs) {
368  return *this;
369  }
370 
371  if (size_ != rhs.size_) {
372  delete [] data_;
373  allocate(rhs.nrows_, rhs.ncols_, rhs.nslices_);
374  }
375 
376  memcpy(data_, rhs.data_, sizeof(T) * size_);
377 
378  return *this;
379  }
380 
381  /// @brief Get the array value at (row,col).
382  const T& operator()(const int row, const int col, const int slice) const
383  {
384  #ifdef Array3_check_enabled
385  check_index(row, col, slice);
386  #endif
387  return data_[row + col*nrows_ + slice*slice_size_];
388  }
389 
390  /// @brief Set the array value at (row,col).
391  T& operator()(const int row, const int col, const int slice)
392  {
393  #ifdef Array3_check_enabled
394  check_index(row, col, slice);
395  #endif
396  return data_[row + col*nrows_ + slice*slice_size_];
397  }
398 
399  Array3& operator=(const double value)
400  {
401  for (int i = 0; i < size_; i++) {
402  data_[i] = value;
403  }
404  return *this;
405  }
406 
407  ///////////////////////////////////////////////////
408  // M a t h e m a t i c a l o p e r a t o r s //
409  ///////////////////////////////////////////////////
410 
411  /// @brief Multiply by a scalar.
412  Array3<T> operator * (const T value) const
413  {
414  Array3<T> result(nrows_, ncols_, nslices_);
415  for (int i = 0; i < size_; i++) {
416  result.data_[i] = value * data_[i];
417  }
418  return result;
419  }
420 
421  Array3<T>& operator *= (const T value)
422  {
423  for (int i = 0; i < size_; i++) {
424  data_[i] *= value;
425  }
426  return *this;
427  }
428 
429  friend const Array3<T> operator * (const T value, const Array3& rhs)
430  {
431  if (rhs.data_ == nullptr) {
432  throw std::runtime_error("Null data for rhs Array3.");
433  }
434  Array3<T> result(rhs.nrows_, rhs.ncols_, rhs.nslices_);
435  for (int i = 0; i < rhs.size_; i++) {
436  result.data_[i] = value * rhs.data_[i];
437  }
438  return result;
439  }
440 
441  T* data() const {
442  return data_;
443  }
444 
445  private:
446  int nrows_ = 0;
447  int ncols_ = 0;
448  int nslices_ = 0;
449  int slice_size_ = 0;
450  int size_ = 0;
451  T *data_ = nullptr;
452 
453 };
454 
455 #endif
456 
The Array3 template class implements a simple interface to 3D arrays.
Definition: Array3.h:51
const T & operator()(const int row, const int col, const int slice) const
Get the array value at (row,col).
Definition: Array3.h:382
void clear()
Free the array data.
Definition: Array3.h:165
Array< T > slice(const int slice) const
Get a slice.
Definition: Array3.h:210
Array3 & operator=(const Array3 &rhs)
Array assignment.
Definition: Array3.h:357
void set_values(Array< T > &rhs)
Set the array values from an Array with the equivalent number of values.
Definition: Array3.h:295
Array< T > rslice(const int slice) const
rslice
Definition: Array3.h:184
void resize(const int num_rows, const int num_cols, const int num_slices)
Resize the array.
Definition: Array3.h:271
bool allocated()
Test if an array has rows or columns or slices set but no data.
Definition: Array3.h:256
Array3< T > operator*(const T value) const
Multiply by a scalar.
Definition: Array3.h:412
T & operator()(const int row, const int col, const int slice)
Set the array value at (row,col).
Definition: Array3.h:391
Array3(const Array3 &rhs)
Array copy.
Definition: Array3.h:92