libROM  v1.0
Data-driven physical simulation library
Vector.cpp
1 
11 // Description: A simple, parallel Vector class with the utility needed to
12 // support the basis generation methods of this library. A
13 // distributed Vector has its rows distributed across processors.
14 
15 #include "Vector.h"
16 #include "utils/HDFDatabase.h"
17 #include "utils/mpi_utils.h"
18 
19 #include "mpi.h"
20 
21 #include <cmath>
22 #include <string.h>
23 
24 namespace CAROM {
25 
26 Vector::Vector() :
27  d_vec(NULL),
28  d_alloc_size(0),
29  d_distributed(false),
30  d_owns_data(true)
31 {}
32 
33 Vector::Vector(
34  int dim,
35  bool distributed) :
36  d_vec(NULL),
37  d_alloc_size(0),
38  d_distributed(distributed),
39  d_owns_data(true)
40 {
41  CAROM_VERIFY(dim > 0);
42  setSize(dim);
43  int mpi_init;
44  MPI_Initialized(&mpi_init);
45  if (mpi_init) {
46  MPI_Comm_size(MPI_COMM_WORLD, &d_num_procs);
47  MPI_Comm_rank(MPI_COMM_WORLD, &d_rank);
48  }
49  else {
50  d_num_procs = 1;
51  d_rank = 0;
52  }
53 }
54 
55 Vector::Vector(
56  double* vec,
57  int dim,
58  bool distributed,
59  bool copy_data) :
60  d_vec(NULL),
61  d_alloc_size(0),
62  d_distributed(distributed),
63  d_owns_data(copy_data)
64 {
65  CAROM_VERIFY(vec != 0);
66  CAROM_VERIFY(dim > 0);
67  if (copy_data) {
68  setSize(dim);
69  memcpy(d_vec, vec, d_alloc_size*sizeof(double));
70  }
71  else {
72  d_vec = vec;
73  d_alloc_size = dim;
74  d_dim = dim;
75  }
76  int mpi_init;
77  MPI_Initialized(&mpi_init);
78  if (mpi_init) {
79  MPI_Comm_size(MPI_COMM_WORLD, &d_num_procs);
80  MPI_Comm_rank(MPI_COMM_WORLD, &d_rank);
81  }
82  else {
83  d_num_procs = 1;
84  d_rank = 0;
85  }
86 }
87 
88 Vector::Vector(
89  const Vector& other) :
90  d_vec(NULL),
91  d_alloc_size(0),
92  d_distributed(other.d_distributed),
93  d_owns_data(true)
94 {
95  setSize(other.d_dim);
96  int mpi_init;
97  MPI_Initialized(&mpi_init);
98  if (mpi_init) {
99  MPI_Comm_size(MPI_COMM_WORLD, &d_num_procs);
100  MPI_Comm_rank(MPI_COMM_WORLD, &d_rank);
101  }
102  else {
103  d_num_procs = 1;
104  d_rank = 0;
105  }
106  memcpy(d_vec, other.d_vec, d_alloc_size*sizeof(double));
107 }
108 
110 {
111  if (d_owns_data && d_vec) {
112  delete [] d_vec;
113  }
114 }
115 
116 Vector&
118  const Vector& rhs)
119 {
120  d_distributed = rhs.d_distributed;
121  d_num_procs = rhs.d_num_procs;
122  setSize(rhs.d_dim);
123  memcpy(d_vec, rhs.d_vec, d_dim*sizeof(double));
124  return *this;
125 }
126 
127 Vector&
129  const Vector& rhs)
130 {
131  CAROM_ASSERT(d_dim == rhs.d_dim);
132  for(int i=0; i<d_dim; ++i) d_vec[i] += rhs.d_vec[i];
133  return *this;
134 }
135 
136 Vector&
138  const Vector& rhs)
139 {
140  CAROM_ASSERT(d_dim == rhs.d_dim);
141  for(int i=0; i<d_dim; ++i) d_vec[i] -= rhs.d_vec[i];
142  return *this;
143 }
144 
145 Vector&
146 Vector::operator = (const double& a)
147 {
148  for(int i=0; i<d_dim; ++i) d_vec[i] = a;
149  return *this;
150 }
151 
152 Vector&
153 Vector::operator *= (const double& a)
154 {
155  for(int i=0; i<d_dim; ++i) d_vec[i] *= a;
156  return *this;
157 }
158 
159 Vector&
160 Vector::transform(std::function<void(const int size, double* vector)>
161  transformer) {
162  transformer(d_dim, d_vec);
163  return *this;
164 }
165 
166 void
168  std::function<void(const int size, double* vector)> transformer) const {
169  result.setSize(d_dim);
170  result.d_distributed = d_distributed;
171  transformer(d_dim, result.d_vec);
172 }
173 
174 Vector&
176  std::function<void(const int size, double* origVector, double* resultVector)>
177  transformer) {
178  Vector* origVector = new Vector(*this);
179  transformer(d_dim, origVector->d_vec, d_vec);
180  delete origVector;
181  return *this;
182 }
183 
184 void
186  std::function<void(const int size, double* origVector, double* resultVector)>
187  transformer) const {
188  Vector* origVector = new Vector(*this);
189  result.setSize(d_dim);
190  result.d_distributed = d_distributed;
191  transformer(d_dim, origVector->d_vec, result.d_vec);
192  delete origVector;
193 }
194 
195 double
197  const Vector& other) const
198 {
199  CAROM_ASSERT(dim() == other.dim());
200  CAROM_ASSERT(distributed() == other.distributed());
201  double ip;
202  double local_ip = 0.0;
203  for (int i = 0; i < d_dim; ++i) {
204  local_ip += d_vec[i]*other.d_vec[i];
205  }
206  if (d_num_procs > 1 && d_distributed) {
207  MPI_Allreduce(&local_ip, &ip, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
208  }
209  else {
210  ip = local_ip;
211  }
212  return ip;
213 }
214 
215 double
217 {
218  double norm = sqrt(norm2());
219  return norm;
220 }
221 
222 double
224 {
225  double norm2 = inner_product(*this);
226  return norm2;
227 }
228 
229 double
231 {
232  double Norm = norm();
233  for (int i = 0; i < d_dim; ++i) {
234  d_vec[i] /= Norm;
235  }
236  return Norm;
237 }
238 
239 void
241  const Vector& other,
242  Vector& result) const
243 {
244  CAROM_ASSERT(result.distributed() == distributed());
245  CAROM_ASSERT(distributed() == other.distributed());
246  CAROM_ASSERT(dim() == other.dim());
247 
248  // Size result correctly.
249  result.setSize(d_dim);
250 
251  // Do the addition.
252  for (int i = 0; i < d_dim; ++i) {
253  result.d_vec[i] = d_vec[i] + other.d_vec[i];
254  }
255 }
256 
257 void
259  double factor,
260  const Vector& other,
261  Vector& result) const
262 {
263  CAROM_ASSERT(result.distributed() == distributed());
264  CAROM_ASSERT(distributed() == other.distributed());
265  CAROM_ASSERT(dim() == other.dim());
266 
267  // Size result correctly.
268  result.setSize(d_dim);
269 
270  // Do the addition.
271  for (int i = 0; i < d_dim; ++i) {
272  result.d_vec[i] = d_vec[i] + factor*other.d_vec[i];
273  }
274 }
275 
276 void
278  double factor,
279  const Vector& other)
280 {
281  CAROM_ASSERT(distributed() == other.distributed());
282  CAROM_ASSERT(dim() == other.dim());
283 
284  // Do the addition.
285  for (int i = 0; i < d_dim; ++i) {
286  d_vec[i] += factor*other.d_vec[i];
287  }
288 }
289 
290 void
292  const Vector& other,
293  Vector& result) const
294 {
295  CAROM_ASSERT(result.distributed() == distributed());
296  CAROM_ASSERT(distributed() == other.distributed());
297  CAROM_ASSERT(dim() == other.dim());
298 
299  // Size result correctly.
300  result.setSize(d_dim);
301 
302  // Do the subtraction.
303  for (int i = 0; i < d_dim; ++i) {
304  result.d_vec[i] = d_vec[i] - other.d_vec[i];
305  }
306 }
307 
308 void
310  double factor,
311  Vector& result) const
312 {
313  CAROM_ASSERT(result.distributed() == distributed());
314 
315  // Size result correctly.
316  result.setSize(d_dim);
317 
318  // Do the multiplication.
319  for (int i = 0; i < d_dim; ++i) {
320  result.d_vec[i] = factor*d_vec[i];
321  }
322 }
323 
324 void
325 Vector::write(const std::string& base_file_name)
326 {
327  CAROM_ASSERT(!base_file_name.empty());
328 
329  int mpi_init;
330  MPI_Initialized(&mpi_init);
331  int rank;
332  if (mpi_init) {
333  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
334  }
335  else {
336  rank = 0;
337  }
338 
339  char tmp[100];
340  sprintf(tmp, ".%06d", rank);
341  std::string full_file_name = base_file_name + tmp;
342  HDFDatabase database;
343  database.create(full_file_name);
344 
345  sprintf(tmp, "distributed");
346  database.putInteger(tmp, d_distributed);
347  sprintf(tmp, "dim");
348  database.putInteger(tmp, d_dim);
349  sprintf(tmp, "data");
350  database.putDoubleArray(tmp, d_vec, d_dim);
351  database.close();
352 }
353 
354 void
355 Vector::print(const char * prefix) const
356 {
357  int my_rank;
358  const bool success = MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
359  CAROM_ASSERT(success);
360 
361  std::string filename_str = prefix + std::to_string(my_rank);
362  const char * filename = filename_str.c_str();
363  FILE * pFile = fopen(filename,"w");
364  for (int k = 0; k < d_dim; ++k) {
365  fprintf(pFile, " %25.20e\n", d_vec[k]);
366  }
367  fclose(pFile);
368 }
369 
370 void
371 Vector::read(const std::string& base_file_name)
372 {
373  CAROM_ASSERT(!base_file_name.empty());
374 
375  int mpi_init;
376  MPI_Initialized(&mpi_init);
377  int rank;
378  if (mpi_init) {
379  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
380  }
381  else {
382  rank = 0;
383  }
384 
385  char tmp[100];
386  sprintf(tmp, ".%06d", rank);
387  std::string full_file_name = base_file_name + tmp;
388  HDFDatabase database;
389  database.open(full_file_name, "r");
390 
391  sprintf(tmp, "distributed");
392  int distributed;
393  database.getInteger(tmp, distributed);
394  d_distributed = bool(distributed);
395  int dim;
396  sprintf(tmp, "dim");
397  database.getInteger(tmp, dim);
398  setSize(dim);
399  sprintf(tmp, "data");
400  database.getDoubleArray(tmp, d_vec, d_alloc_size);
401  d_owns_data = true;
402  if (mpi_init) {
403  MPI_Comm_size(MPI_COMM_WORLD, &d_num_procs);
404  }
405  else {
406  d_num_procs = 1;
407  }
408  database.close();
409 }
410 
411 void
412 Vector::local_read(const std::string& base_file_name, int rank)
413 {
414  CAROM_ASSERT(!base_file_name.empty());
415 
416  int mpi_init;
417  MPI_Initialized(&mpi_init);
418 
419  char tmp[100];
420  sprintf(tmp, ".%06d", rank);
421  std::string full_file_name = base_file_name + tmp;
422  HDFDatabase database;
423  database.open(full_file_name, "r");
424 
425  sprintf(tmp, "distributed");
426  int distributed;
427  database.getInteger(tmp, distributed);
428  d_distributed = bool(distributed);
429  int dim;
430  sprintf(tmp, "dim");
431  database.getInteger(tmp, dim);
432  setSize(dim);
433  sprintf(tmp, "data");
434  database.getDoubleArray(tmp, d_vec, d_alloc_size);
435  d_owns_data = true;
436  if (mpi_init) {
437  MPI_Comm_size(MPI_COMM_WORLD, &d_num_procs);
438  }
439  else {
440  d_num_procs = 1;
441  }
442  database.close();
443 }
444 
445 double Vector::localMin(int nmax)
446 {
447  const int n = nmax > 0 ? nmax : d_dim;
448  double v = d_vec[0];
449 
450  for (int i=1; i<n; ++i)
451  {
452  if (d_vec[i] < v)
453  v = d_vec[i];
454  }
455 
456  return v;
457 }
458 
459 void
460 Vector::distribute(const int local_dim)
461 {
462  CAROM_VERIFY(!distributed());
463  CAROM_VERIFY(d_owns_data);
464  CAROM_VERIFY(local_dim >= 0);
465 
466  std::vector<int> row_offsets;
467  int global_dim = get_global_offsets(local_dim, row_offsets,
468  MPI_COMM_WORLD);
469  CAROM_VERIFY(global_dim == d_dim);
470  int local_offset = row_offsets[d_rank];
471  const int new_size = local_dim;
472 
473  double *d_new_vec = new double [new_size];
474  if (new_size > 0)
475  memcpy(d_new_vec, &d_vec[local_offset], 8 * new_size);
476 
477  delete [] d_vec;
478  d_vec = d_new_vec;
479  d_alloc_size = new_size;
480  d_dim = new_size;
481 
482  d_distributed = true;
483 }
484 
485 void
487 {
488  CAROM_VERIFY(distributed());
489  CAROM_VERIFY(d_owns_data);
490 
491  std::vector<int> row_offsets;
492  const int global_dim = get_global_offsets(d_dim, row_offsets,
493  MPI_COMM_WORLD);
494  const int new_size = global_dim;
495 
496  int *data_offsets = new int[row_offsets.size() - 1];
497  int *data_cnts = new int[row_offsets.size() - 1];
498  for (int k = 0; k < row_offsets.size() - 1; k++)
499  {
500  data_offsets[k] = row_offsets[k];
501  data_cnts[k] = (row_offsets[k+1] - row_offsets[k]);
502  }
503 
504  double *d_new_vec = new double [new_size] {0.0};
505  CAROM_VERIFY(MPI_Allgatherv(d_vec, d_dim, MPI_DOUBLE,
506  d_new_vec, data_cnts, data_offsets, MPI_DOUBLE,
507  MPI_COMM_WORLD) == MPI_SUCCESS);
508 
509  delete [] d_vec;
510  delete [] data_offsets;
511  delete [] data_cnts;
512  d_vec = d_new_vec;
513  d_alloc_size = new_size;
514  d_dim = new_size;
515 
516  d_distributed = false;
517 }
518 
519 int getCenterPoint(const std::vector<const Vector*>& points,
520  bool use_centroid)
521 {
522  int center_point;
523 
524  // get center-most point
525  // obtain the centroid and find the point closest to centroid
526  if (use_centroid)
527  {
528  Vector centroid(*points[0]);
529  for (int i = 1; i < points.size(); i++) {
530  centroid += *points[i];
531  }
532  centroid.mult(1.0 / points.size(), centroid);
533 
534  double min_dist_to_centroid;
535  for (int i = 0; i < points.size(); i++) {
536  Vector diff;
537  centroid.minus(*points[i], diff);
538  double dist_to_centroid = diff.norm();
539  if (i == 0 || dist_to_centroid < min_dist_to_centroid)
540  {
541  min_dist_to_centroid = dist_to_centroid;
542  center_point = i;
543  }
544  }
545  }
546 
547  // otherwise, do a brute force search to obtain
548  // the point closest to all other points
549  else
550  {
551  std::vector<double> dist_to_points(points.size(), 0);
552  for (int i = 0; i < points.size(); i++) {
553  for (int j = i + 1; j < points.size(); j++) {
554  Vector diff;
555  points[i]->minus(*points[j], diff);
556  double dist = diff.norm();
557  dist_to_points[i] += dist;
558  dist_to_points[j] += dist;
559  }
560  }
561 
562  double closest_dist_to_points = INT_MAX;
563  for (int i = 0; i < dist_to_points.size(); i++) {
564  if (dist_to_points[i] < closest_dist_to_points)
565  {
566  closest_dist_to_points = dist_to_points[i];
567  center_point = i;
568  }
569  }
570  }
571 
572  return center_point;
573 }
574 
575 int getCenterPoint(const std::vector<Vector>& points,
576  bool use_centroid)
577 {
578  std::vector<const Vector*> temp_points;
579  for (int i = 0; i < points.size(); i++)
580  {
581  temp_points.push_back(&points[i]);
582  }
583  return getCenterPoint(temp_points, use_centroid);
584 }
585 
586 int getClosestPoint(const std::vector<const Vector*>& points,
587  const Vector & test_point)
588 {
589  int closest_point = 0;
590  double closest_dist_to_test_point = INT_MAX;
591  for (int i = 0; i < points.size(); i++) {
592  Vector diff;
593  test_point.minus(*points[i], diff);
594  double dist = diff.norm();
595  if (dist < closest_dist_to_test_point)
596  {
597  closest_dist_to_test_point = dist;
598  closest_point = i;
599  }
600  }
601 
602  return closest_point;
603 }
604 
605 int getClosestPoint(const std::vector<Vector>& points,
606  const Vector & test_point)
607 {
608  std::vector<const Vector*> temp_points;
609  for (int i = 0; i < points.size(); i++)
610  {
611  temp_points.push_back(&points[i]);
612  }
613  return getClosestPoint(temp_points, test_point);
614 }
615 
616 }
void getInteger(const std::string &key, int &data)
Reads an integer associated with the supplied key from the currently open database file.
Definition: Database.h:181
void putInteger(const std::string &key, int data)
Writes an integer associated with the supplied key to currently open database file.
Definition: Database.h:94
virtual void getDoubleArray(const std::string &key, double *data, int nelements, const bool distributed=false) override
Reads an array of doubles associated with the supplied key from the currently open HDF5 database file...
virtual bool create(const std::string &file_name, const MPI_Comm comm=MPI_COMM_NULL) override
Creates a new HDF5 database file with the supplied name.
Definition: HDFDatabase.cpp:45
virtual bool open(const std::string &file_name, const std::string &type, const MPI_Comm comm=MPI_COMM_NULL) override
Opens an existing HDF5 database file with the supplied name.
Definition: HDFDatabase.cpp:76
virtual void putDoubleArray(const std::string &key, const double *const data, int nelements, const bool distributed=false) override
Writes an array of doubles associated with the supplied key to the currently open HDF5 database file.
virtual bool close() override
Closes the currently open HDF5 database file.
void setSize(int dim)
Sets the length of the vector and reallocates storage if needed. All values are initialized to zero.
Definition: Vector.h:217
void read(const std::string &base_file_name)
read Vector from (a) HDF file(s).
Definition: Vector.cpp:371
Vector & operator-=(const Vector &rhs)
Subtraction operator.
Definition: Vector.cpp:137
bool distributed() const
Returns true if the Vector is distributed.
Definition: Vector.h:240
void print(const char *prefix) const
print Vector into (a) ascii file(s).
Definition: Vector.cpp:355
void distribute(const int local_dim)
Distribute this vector among MPI processes, based on the specified local dimension....
Definition: Vector.cpp:460
double norm() const
Form the norm of this.
Definition: Vector.cpp:216
double normalize()
Normalizes the Vector and returns its norm.
Definition: Vector.cpp:230
std::unique_ptr< Vector > minus(const Vector &other) const
Subtracts other and this and returns the result.
Definition: Vector.h:402
void write(const std::string &base_file_name)
write Vector into (a) HDF file(s).
Definition: Vector.cpp:325
std::unique_ptr< Vector > plus(const Vector &other) const
Adds other and this and returns the result.
Definition: Vector.h:313
std::unique_ptr< Vector > plusAx(double factor, const Vector &other) const
Adds factor*other and this and returns the result.
Definition: Vector.h:349
Vector & operator=(const Vector &rhs)
Assignment operator.
Definition: Vector.cpp:117
double inner_product(const Vector &other) const
Inner product, reference form.
Definition: Vector.cpp:196
~Vector()
Destructor.
Definition: Vector.cpp:109
void plusEqAx(double factor, const Vector &other)
Adds factor*other to this.
Definition: Vector.cpp:277
std::unique_ptr< Vector > mult(double factor) const
Multiplies this by the supplied constant and returns the result.
Definition: Vector.h:436
double norm2() const
Form the squared norm of this.
Definition: Vector.cpp:223
Vector & transform(std::function< void(const int size, double *vector)> transformer)
Transform the vector using a supplied function.
Definition: Vector.cpp:160
Vector & operator+=(const Vector &rhs)
Addition operator.
Definition: Vector.cpp:128
double localMin(int nmax=0)
Compute the local minimum of this.
Definition: Vector.cpp:445
void local_read(const std::string &base_file_name, int rank)
read read a single rank of a distributed Vector from (a) HDF file(s).
Definition: Vector.cpp:412
int dim() const
Returns the dimension of the Vector on this processor.
Definition: Vector.h:251
Vector & operator*=(const double &a)
Scaling operator.
Definition: Vector.cpp:153
void gather()
Gather all the distributed elements among MPI processes. This becomes not distributed after this func...
Definition: Vector.cpp:486
int getCenterPoint(const std::vector< const Vector * > &points, bool use_centroid)
Get center point of a group of points.
Definition: Vector.cpp:519
int get_global_offsets(const int local_dim, std::vector< int > &offsets, const MPI_Comm &comm)
Save integer offsets for each MPI rank under MPI communicator comm, where each rank as the size of lo...
Definition: mpi_utils.cpp:41
int getClosestPoint(const std::vector< const Vector * > &points, const Vector &test_point)
Get closest point to a test point among a group of points.
Definition: Vector.cpp:586