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