libROM  v1.0
Data-driven physical simulation library
DMD.cpp
1 
11 // Description: Implementation of the DMD algorithm.
12 
13 #include "DMD.h"
14 
15 #include "linalg/Matrix.h"
16 #include "linalg/Vector.h"
17 #include "linalg/scalapack_wrapper.h"
18 #include "utils/Utilities.h"
19 #include "utils/CSVDatabase.h"
20 #include "utils/HDFDatabase.h"
21 #include "mpi.h"
22 
23 #include <cstring>
24 
25 /* Use C++11 built-in shared pointers if available; else fallback to Boost. */
26 #if __cplusplus >= 201103L
27 #include <memory>
28 #else
29 #include <boost/shared_ptr.hpp>
30 #endif
31 
32 /* Use automatically detected Fortran name-mangling scheme */
33 #define zgetrf CAROM_FC_GLOBAL(zgetrf, ZGETRF)
34 #define zgetri CAROM_FC_GLOBAL(zgetri, ZGETRI)
35 
36 extern "C" {
37  // LU decomposition of a general matrix.
38  void zgetrf(int*, int*, double*, int*, int*, int*);
39 
40  // Generate inverse of a matrix given its LU decomposition.
41  void zgetri(int*, double*, int*, int*, double*, int*, int*);
42 }
43 
44 namespace CAROM {
45 
46 DMD::DMD(int dim, bool alt_output_basis, Vector* state_offset)
47 {
48  CAROM_VERIFY(dim > 0);
49 
50  // Get the rank of this process, and the number of processors.
51  int mpi_init;
52  MPI_Initialized(&mpi_init);
53  if (mpi_init == 0) {
54  MPI_Init(nullptr, nullptr);
55  }
56 
57  MPI_Comm_rank(MPI_COMM_WORLD, &d_rank);
58  MPI_Comm_size(MPI_COMM_WORLD, &d_num_procs);
59  d_dim = dim;
60  d_trained = false;
61  d_init_projected = false;
62  d_alt_output_basis = alt_output_basis;
63  setOffset(state_offset, 0);
64 }
65 
66 DMD::DMD(int dim, double dt, bool alt_output_basis, Vector* state_offset)
67 {
68  CAROM_VERIFY(dim > 0);
69  CAROM_VERIFY(dt > 0.0);
70 
71  // Get the rank of this process, and the number of processors.
72  int mpi_init;
73  MPI_Initialized(&mpi_init);
74  if (mpi_init == 0) {
75  MPI_Init(nullptr, nullptr);
76  }
77 
78  MPI_Comm_rank(MPI_COMM_WORLD, &d_rank);
79  MPI_Comm_size(MPI_COMM_WORLD, &d_num_procs);
80  d_dim = dim;
81  d_dt = dt;
82  d_trained = false;
83  d_init_projected = false;
84  d_alt_output_basis = alt_output_basis;
85  setOffset(state_offset, 0);
86 }
87 
88 DMD::DMD(std::string base_file_name)
89 {
90  // Get the rank of this process, and the number of processors.
91  int mpi_init;
92  MPI_Initialized(&mpi_init);
93  if (mpi_init == 0) {
94  MPI_Init(nullptr, nullptr);
95  }
96 
97  MPI_Comm_rank(MPI_COMM_WORLD, &d_rank);
98  MPI_Comm_size(MPI_COMM_WORLD, &d_num_procs);
99  d_trained = true;
100  d_init_projected = true;
101 
102  load(base_file_name);
103 }
104 
105 DMD::DMD(std::vector<std::complex<double>> eigs, Matrix* phi_real,
106  Matrix* phi_imaginary, int k,
107  double dt, double t_offset, Vector* state_offset)
108 {
109  // Get the rank of this process, and the number of processors.
110  int mpi_init;
111  MPI_Initialized(&mpi_init);
112  if (mpi_init == 0) {
113  MPI_Init(nullptr, nullptr);
114  }
115 
116  MPI_Comm_rank(MPI_COMM_WORLD, &d_rank);
117  MPI_Comm_size(MPI_COMM_WORLD, &d_num_procs);
118  d_trained = true;
119  d_init_projected = false;
120 
121  d_eigs = eigs;
122  d_phi_real = phi_real;
123  d_phi_imaginary = phi_imaginary;
124  d_k = k;
125  d_dt = dt;
126  d_t_offset = t_offset;
127  setOffset(state_offset, 0);
128 }
129 
131 {
132  for (auto snapshot : d_snapshots)
133  {
134  delete snapshot;
135  }
136  for (auto sampled_time : d_sampled_times)
137  {
138  delete sampled_time;
139  }
140  delete d_state_offset;
141  delete d_basis;
142  delete d_A_tilde;
143  delete d_phi_real;
144  delete d_phi_imaginary;
147  delete d_projected_init_real;
149 }
150 
151 void DMD::setOffset(Vector* offset_vector, int order)
152 {
153  if (order == 0)
154  {
155  d_state_offset = offset_vector;
156  }
157 }
158 
159 void DMD::takeSample(double* u_in, double t)
160 {
161  CAROM_VERIFY(u_in != 0);
162  CAROM_VERIFY(t >= 0.0);
163  Vector* sample = new Vector(u_in, d_dim, true);
164 
165  double orig_t = t;
166  if (d_snapshots.empty())
167  {
168  d_t_offset = t;
169  t = 0.0;
170  }
171  else
172  {
173  t -= d_t_offset;
174  }
175 
176  // Erase any snapshots taken at the same or later time
177  while (!d_sampled_times.empty() && d_sampled_times.back()->item(0) >= t)
178  {
179  if (d_rank == 0) std::cout << "Removing existing snapshot at time: " <<
180  d_t_offset + d_sampled_times.back()->item(0) << std::endl;
181  Vector* last_snapshot = d_snapshots.back();
182  delete last_snapshot;
183  d_snapshots.pop_back();
184  d_sampled_times.pop_back();
185  }
186 
187  if (d_snapshots.empty())
188  {
189  d_t_offset = orig_t;
190  t = 0.0;
191  }
192  else
193  {
194  CAROM_VERIFY(d_sampled_times.back()->item(0) < t);
195  }
196 
197  d_snapshots.push_back(sample);
198 
199  Vector* sampled_time = new Vector(&t, 1, false);
200  d_sampled_times.push_back(sampled_time);
201 }
202 
203 void DMD::train(double energy_fraction, const Matrix* W0, double linearity_tol)
204 {
205  const Matrix* f_snapshots = getSnapshotMatrix();
206  CAROM_VERIFY(f_snapshots->numColumns() > 1);
207  CAROM_VERIFY(energy_fraction > 0 && energy_fraction <= 1);
208  d_energy_fraction = energy_fraction;
209  constructDMD(f_snapshots, d_rank, d_num_procs, W0, linearity_tol);
210 
211  delete f_snapshots;
212 }
213 
214 void DMD::train(int k, const Matrix* W0, double linearity_tol)
215 {
216  const Matrix* f_snapshots = getSnapshotMatrix();
217  CAROM_VERIFY(f_snapshots->numColumns() > 1);
218  CAROM_VERIFY(k > 0 && k <= f_snapshots->numColumns() - 1);
219  d_energy_fraction = -1.0;
220  d_k = k;
221  constructDMD(f_snapshots, d_rank, d_num_procs, W0, linearity_tol);
222 
223  delete f_snapshots;
224 }
225 
226 std::pair<Matrix*, Matrix*>
228 {
229  CAROM_VERIFY(snapshots->numColumns() > 1);
230 
231  // TODO: Making two copies of the snapshot matrix has a lot of overhead.
232  // We need to figure out a way to do submatrix multiplication and to
233  // reimplement this algorithm using one snapshot matrix.
234  Matrix* f_snapshots_in = new Matrix(snapshots->numRows(),
235  snapshots->numColumns() - 1, snapshots->distributed());
236  Matrix* f_snapshots_out = new Matrix(snapshots->numRows(),
237  snapshots->numColumns() - 1, snapshots->distributed());
238 
239  // Break up snapshots into snapshots_in and snapshots_out
240  // snapshots_in = all columns of snapshots except last
241  // snapshots_out = all columns of snapshots except first
242  for (int i = 0; i < snapshots->numRows(); i++)
243  {
244  for (int j = 0; j < snapshots->numColumns() - 1; j++)
245  {
246  f_snapshots_in->item(i, j) = snapshots->item(i, j);
247  f_snapshots_out->item(i, j) = snapshots->item(i, j + 1);
248  if (d_state_offset)
249  {
250  f_snapshots_in->item(i, j) -= d_state_offset->item(i);
251  f_snapshots_out->item(i, j) -= d_state_offset->item(i);
252  }
253  }
254  }
255 
256  return std::pair<Matrix*,Matrix*>(f_snapshots_in, f_snapshots_out);
257 }
258 
259 void
260 DMD::computePhi(struct DMDInternal dmd_internal_obj)
261 {
262  // Calculate phi
263  if (d_alt_output_basis)
264  {
265  Matrix* f_snapshots_out_mult_d_basis_right =
266  dmd_internal_obj.snapshots_out->mult(dmd_internal_obj.basis_right);
267  Matrix* f_snapshots_out_mult_d_basis_right_mult_d_S_inv =
268  f_snapshots_out_mult_d_basis_right->mult(dmd_internal_obj.S_inv);
269  d_phi_real = f_snapshots_out_mult_d_basis_right_mult_d_S_inv->mult(
270  dmd_internal_obj.eigenpair->ev_real);
271  d_phi_imaginary = f_snapshots_out_mult_d_basis_right_mult_d_S_inv->mult(
272  dmd_internal_obj.eigenpair->ev_imaginary);
273 
274  delete f_snapshots_out_mult_d_basis_right;
275  delete f_snapshots_out_mult_d_basis_right_mult_d_S_inv;
276  }
277  else
278  {
279  d_phi_real = dmd_internal_obj.basis->mult(dmd_internal_obj.eigenpair->ev_real);
280  d_phi_imaginary = dmd_internal_obj.basis->mult(
281  dmd_internal_obj.eigenpair->ev_imaginary);
282  }
283 }
284 
285 void
286 DMD::constructDMD(const Matrix* f_snapshots,
287  int d_rank,
288  int d_num_procs,
289  const Matrix* W0,
290  double linearity_tol)
291 {
292  std::pair<Matrix*, Matrix*> f_snapshot_pair = computeDMDSnapshotPair(
293  f_snapshots);
294  Matrix* f_snapshots_in = f_snapshot_pair.first;
295  Matrix* f_snapshots_out = f_snapshot_pair.second;
296 
297  int *row_offset = new int[d_num_procs + 1];
298  row_offset[d_num_procs] = f_snapshots_in->numDistributedRows();
299  row_offset[d_rank] = f_snapshots_in->numRows();
300 
301  CAROM_VERIFY(MPI_Allgather(MPI_IN_PLACE,
302  1,
303  MPI_INT,
304  row_offset,
305  1,
306  MPI_INT,
307  MPI_COMM_WORLD) == MPI_SUCCESS);
308  for (int i = d_num_procs - 1; i >= 0; i--) {
309  row_offset[i] = row_offset[i + 1] - row_offset[i];
310  }
311 
312  CAROM_VERIFY(row_offset[0] == 0);
313 
314  int d_blocksize = row_offset[d_num_procs] / d_num_procs;
315  if (row_offset[d_num_procs] % d_num_procs != 0) d_blocksize += 1;
316 
317  SLPK_Matrix svd_input;
318 
319  // Calculate svd of snapshots_in
320  initialize_matrix(&svd_input, f_snapshots_in->numColumns(),
321  f_snapshots_in->numDistributedRows(),
322  1, d_num_procs, d_blocksize, d_blocksize);
323 
324  for (int rank = 0; rank < d_num_procs; ++rank)
325  {
326  scatter_block(&svd_input, 1, row_offset[rank] + 1,
327  f_snapshots_in->getData(),
328  f_snapshots_in->numColumns(),
329  row_offset[rank + 1] - row_offset[rank], rank);
330  }
331 
332  std::unique_ptr<SVDManager> d_factorizer(new SVDManager);
333 
334  // This block does the actual ScaLAPACK call to do the factorization.
335  svd_init(d_factorizer.get(), &svd_input);
336  d_factorizer->dov = 1;
337  factorize(d_factorizer.get());
338  free_matrix_data(&svd_input);
339 
340  // Compute how many basis vectors we will actually use.
341  d_num_singular_vectors = std::min(f_snapshots_in->numColumns(),
342  f_snapshots_in->numDistributedRows());
343  for (int i = 0; i < d_num_singular_vectors; i++)
344  {
345  d_sv.push_back(d_factorizer->S[i]);
346  }
347 
348  if (d_energy_fraction != -1.0)
349  {
351  if (d_energy_fraction < 1.0)
352  {
353  double total_energy = 0.0;
354  for (int i = 0; i < d_num_singular_vectors; i++)
355  {
356  total_energy += d_factorizer->S[i];
357  }
358  double current_energy = 0.0;
359  for (int i = 0; i < d_num_singular_vectors; i++)
360  {
361  current_energy += d_factorizer->S[i];
362  if (current_energy / total_energy >= d_energy_fraction)
363  {
364  d_k = i + 1;
365  break;
366  }
367  }
368  }
369  }
370 
371  if (d_rank == 0) std::cout << "Using " << d_k << " basis vectors out of " <<
372  d_num_singular_vectors << "." << std::endl;
373 
374  // Allocate the appropriate matrices and gather their elements.
375  d_basis = new Matrix(f_snapshots->numRows(), d_k, f_snapshots->distributed());
376  Matrix* d_S_inv = new Matrix(d_k, d_k, false);
377  Matrix* d_basis_right = new Matrix(f_snapshots_in->numColumns(), d_k, false);
378 
379  for (int d_rank = 0; d_rank < d_num_procs; ++d_rank) {
380  // V is computed in the transposed order so no reordering necessary.
381  gather_block(&d_basis->item(0, 0), d_factorizer->V,
382  1, row_offset[static_cast<unsigned>(d_rank)]+1,
383  d_k, row_offset[static_cast<unsigned>(d_rank) + 1] -
384  row_offset[static_cast<unsigned>(d_rank)],
385  d_rank);
386 
387  // gather_transposed_block does the same as gather_block, but transposes
388  // it; here, it is used to go from column-major to row-major order.
389  gather_transposed_block(&d_basis_right->item(0, 0), d_factorizer->U, 1, 1,
390  f_snapshots_in->numColumns(), d_k, d_rank);
391  }
392  delete[] row_offset;
393 
394  // Get inverse of singular values by multiplying by reciprocal.
395  for (int i = 0; i < d_k; ++i)
396  {
397  d_S_inv->item(i, i) = 1 / d_factorizer->S[static_cast<unsigned>(i)];
398  }
399 
400  Matrix* Q = NULL;
401 
402  // If W0 is not null, we need to ensure it is in the basis of W.
403  if (W0 != NULL)
404  {
405  CAROM_VERIFY(W0->numRows() == d_basis->numRows());
406  CAROM_VERIFY(linearity_tol >= 0.0);
407  std::vector<int> lin_independent_cols_W;
408 
409  // Copy W0 and orthogonalize.
410  Matrix* d_basis_init = new Matrix(f_snapshots->numRows(), W0->numColumns(),
411  true);
412  for (int i = 0; i < d_basis_init->numRows(); i++)
413  {
414  for (int j = 0; j < W0->numColumns(); j++)
415  {
416  d_basis_init->item(i, j) = W0->item(i, j);
417  }
418  }
419  d_basis_init->orthogonalize();
420 
421  Vector W_col(f_snapshots->numRows(), f_snapshots->distributed());
422  Vector l(W0->numColumns(), true);
423  Vector W0l(f_snapshots->numRows(), f_snapshots->distributed());
424  // Find which columns of d_basis are linearly independent from W0
425  for (int j = 0; j < d_basis->numColumns(); j++)
426  {
427  // l = W0' * u
428  for (int i = 0; i < f_snapshots->numRows(); i++)
429  {
430  W_col.item(i) = d_basis->item(i, j);
431  }
432  d_basis_init->transposeMult(W_col, l);
433 
434  // W0l = W0 * l
435  d_basis_init->mult(l, W0l);
436 
437  // Compute k = sqrt(u.u - 2.0*l.l + basisl.basisl) which is ||u -
438  // basisl||_{2}. This is the error in the projection of u into the
439  // reduced order space and subsequent lifting back to the full
440  // order space.
441  double k = W_col.inner_product(W_col) - 2.0*l.inner_product(l) +
442  W0l.inner_product(W0l);
443  if (k <= 0)
444  {
445  k = 0;
446  }
447  else
448  {
449  k = sqrt(k);
450  }
451 
452  // Use k to see if the vector addressed by u is linearly dependent
453  // on the left singular vectors.
454  if (k >= linearity_tol)
455  {
456  lin_independent_cols_W.push_back(j);
457  }
458  }
459  delete d_basis_init;
460 
461  // Add the linearly independent columns of W to W0. Call this new basis W_new.
462  Matrix* d_basis_new = new Matrix(f_snapshots->numRows(),
463  W0->numColumns() + lin_independent_cols_W.size(), true);
464  for (int i = 0; i < d_basis_new->numRows(); i++)
465  {
466  for (int j = 0; j < W0->numColumns(); j++)
467  {
468  d_basis_new->item(i, j) = W0->item(i, j);
469  }
470  for (int j = 0; j < lin_independent_cols_W.size(); j++)
471  {
472  d_basis_new->item(i, j + W0->numColumns()) = d_basis->item(i,
473  lin_independent_cols_W[j]);
474  }
475  }
476 
477  // Orthogonalize W_new.
478  d_basis_new->orthogonalize();
479 
480  // Calculate Q = W* x W_new;
481  Q = d_basis->transposeMult(d_basis_new);
482 
483  delete d_basis;
484  d_basis = d_basis_new;
485 
486  d_k = d_basis_new->numColumns();
487  if (d_rank == 0) std::cout << "After adding W0, now using " << d_k <<
488  " basis vectors." << std::endl;
489  }
490 
491  // Calculate A_tilde = U_transpose * f_snapshots_out * V * inv(S)
492  Matrix* d_basis_mult_f_snapshots_out = d_basis->transposeMult(f_snapshots_out);
493  Matrix* d_basis_mult_f_snapshots_out_mult_d_basis_right =
494  d_basis_mult_f_snapshots_out->mult(d_basis_right);
495  if (Q == NULL)
496  {
497  d_A_tilde = d_basis_mult_f_snapshots_out_mult_d_basis_right->mult(d_S_inv);
498  }
499  else
500  {
501  Matrix* d_basis_mult_f_snapshots_out_mult_d_basis_right_mult_d_S_inv =
502  d_basis_mult_f_snapshots_out_mult_d_basis_right->mult(d_S_inv);
503  d_A_tilde = d_basis_mult_f_snapshots_out_mult_d_basis_right_mult_d_S_inv->mult(
504  Q);
505  delete Q;
506  delete d_basis_mult_f_snapshots_out_mult_d_basis_right_mult_d_S_inv;
507  }
508 
509  // Calculate the right eigenvalues/eigenvectors of A_tilde
511  d_eigs = eigenpair.eigs;
512 
513  struct DMDInternal dmd_internal = {f_snapshots_in, f_snapshots_out, d_basis, d_basis_right, d_S_inv, &eigenpair};
514  computePhi(dmd_internal);
515 
516  Vector* init = new Vector(f_snapshots_in->numRows(), true);
517  for (int i = 0; i < init->dim(); i++)
518  {
519  init->item(i) = f_snapshots_in->item(i, 0);
520  }
521 
522  // Calculate pinv(d_phi) * initial_condition.
524 
525  d_trained = true;
526 
527  delete d_basis_right;
528  delete d_S_inv;
529  delete d_basis_mult_f_snapshots_out;
530  delete d_basis_mult_f_snapshots_out_mult_d_basis_right;
531  delete f_snapshots_in;
532  delete f_snapshots_out;
533  delete eigenpair.ev_real;
534  delete eigenpair.ev_imaginary;
535  delete init;
536 
537  release_context(&svd_input);
538 }
539 
540 void
541 DMD::projectInitialCondition(const Vector* init, double t_offset)
542 {
543  Matrix* d_phi_real_squared = d_phi_real->transposeMult(d_phi_real);
544  Matrix* d_phi_real_squared_2 = d_phi_imaginary->transposeMult(d_phi_imaginary);
545  *d_phi_real_squared += *d_phi_real_squared_2;
546 
547  Matrix* d_phi_imaginary_squared = d_phi_real->transposeMult(d_phi_imaginary);
548  Matrix* d_phi_imaginary_squared_2 = d_phi_imaginary->transposeMult(d_phi_real);
549  *d_phi_imaginary_squared -= *d_phi_imaginary_squared_2;
550 
551  const int dprs_row = d_phi_real_squared->numRows();
552  const int dprs_col = d_phi_real_squared->numColumns();
553  double* inverse_input = new double[dprs_row * dprs_col * 2];
554  for (int i = 0; i < d_phi_real_squared->numRows(); i++)
555  {
556  int k = 0;
557  for (int j = 0; j < d_phi_real_squared->numColumns() * 2; j++)
558  {
559  if (j % 2 == 0)
560  {
561  inverse_input[d_phi_real_squared->numColumns() * 2 * i + j] =
562  d_phi_real_squared->item(i, k);
563  }
564  else
565  {
566  inverse_input[d_phi_imaginary_squared->numColumns() * 2 * i + j] =
567  d_phi_imaginary_squared->item(i, k);
568  k++;
569  }
570  }
571  }
572 
573  // Call lapack routines to do the inversion.
574  // Set up some stuff the lapack routines need.
575  int info;
576  int mtx_size = d_phi_real_squared->numColumns();
577  int lwork = mtx_size*mtx_size*std::max(10,d_num_procs);
578  int* ipiv = new int [mtx_size];
579  double* work = new double [lwork];
580 
581  // Now call lapack to do the inversion.
582  zgetrf(&mtx_size, &mtx_size, inverse_input, &mtx_size, ipiv, &info);
583  zgetri(&mtx_size, inverse_input, &mtx_size, ipiv, work, &lwork, &info);
584 
585  d_phi_real_squared_inverse = d_phi_real_squared;
586  d_phi_imaginary_squared_inverse = d_phi_imaginary_squared;
587 
588  for (int i = 0; i < d_phi_real_squared_inverse->numRows(); i++)
589  {
590  int k = 0;
591  for (int j = 0; j < d_phi_real_squared_inverse->numColumns() * 2; j++)
592  {
593  if (j % 2 == 0)
594  {
596  inverse_input[d_phi_real_squared_inverse->numColumns() * 2 * i + j];
597  }
598  else
599  {
601  inverse_input[d_phi_imaginary_squared_inverse->numColumns() * 2 * i + j];
602  k++;
603  }
604  }
605  }
606 
607  Vector* rhs_real = d_phi_real->transposeMult(init);
608  Vector* rhs_imaginary = d_phi_imaginary->transposeMult(init);
609 
610  Vector* d_projected_init_real_1 = d_phi_real_squared_inverse->mult(rhs_real);
611  Vector* d_projected_init_real_2 = d_phi_imaginary_squared_inverse->mult(
612  rhs_imaginary);
613  d_projected_init_real = d_projected_init_real_1->plus(d_projected_init_real_2);
614 
615  Vector* d_projected_init_imaginary_1 = d_phi_real_squared_inverse->mult(
616  rhs_imaginary);
617  Vector* d_projected_init_imaginary_2 = d_phi_imaginary_squared_inverse->mult(
618  rhs_real);
619  d_projected_init_imaginary = d_projected_init_imaginary_2->minus(
620  d_projected_init_imaginary_1);
621 
622  delete d_phi_real_squared_2;
623  delete d_projected_init_real_1;
624  delete d_projected_init_real_2;
625  delete d_phi_imaginary_squared_2;
626  delete d_projected_init_imaginary_1;
627  delete d_projected_init_imaginary_2;
628  delete rhs_real;
629  delete rhs_imaginary;
630 
631  delete [] inverse_input;
632  delete [] ipiv;
633  delete [] work;
634 
635  if (t_offset >= 0.0)
636  {
637  std::cout << "t_offset is updated from " << d_t_offset <<
638  " to " << t_offset << std::endl;
639  d_t_offset = t_offset;
640  }
641  d_init_projected = true;
642 }
643 
644 Vector*
645 DMD::predict(double t, int deg)
646 {
647  CAROM_VERIFY(d_trained);
648  CAROM_VERIFY(d_init_projected);
649  CAROM_VERIFY(t >= 0.0);
650 
651  t -= d_t_offset;
652 
653  std::pair<Matrix*, Matrix*> d_phi_pair = phiMultEigs(t, deg);
654  Matrix* d_phi_mult_eigs_real = d_phi_pair.first;
655  Matrix* d_phi_mult_eigs_imaginary = d_phi_pair.second;
656 
657  Vector* d_predicted_state_real_1 = d_phi_mult_eigs_real->mult(
659  Vector* d_predicted_state_real_2 = d_phi_mult_eigs_imaginary->mult(
661  Vector* d_predicted_state_real = d_predicted_state_real_1->minus(
662  d_predicted_state_real_2);
663  addOffset(d_predicted_state_real, t, deg);
664 
665  delete d_phi_mult_eigs_real;
666  delete d_phi_mult_eigs_imaginary;
667  delete d_predicted_state_real_1;
668  delete d_predicted_state_real_2;
669 
670  return d_predicted_state_real;
671 }
672 
673 void
674 DMD::addOffset(Vector*& result, double t, int deg)
675 {
676  if (d_state_offset)
677  {
678  *result += *d_state_offset;
679  }
680 }
681 
682 std::complex<double>
683 DMD::computeEigExp(std::complex<double> eig, double t)
684 {
685  return std::pow(eig, t / d_dt);
686 }
687 
688 std::pair<Matrix*, Matrix*>
689 DMD::phiMultEigs(double t, int deg)
690 {
691  Matrix* d_eigs_exp_real = new Matrix(d_k, d_k, false);
692  Matrix* d_eigs_exp_imaginary = new Matrix(d_k, d_k, false);
693 
694  for (int i = 0; i < d_k; i++)
695  {
696  std::complex<double> eig_exp = computeEigExp(d_eigs[i], t);
697  for (int k = 0; k < deg; ++k)
698  {
699  eig_exp *= d_eigs[i];
700  }
701  d_eigs_exp_real->item(i, i) = std::real(eig_exp);
702  d_eigs_exp_imaginary->item(i, i) = std::imag(eig_exp);
703  }
704 
705  Matrix* d_phi_mult_eigs_real = d_phi_real->mult(d_eigs_exp_real);
706  Matrix* d_phi_mult_eigs_real_2 = d_phi_imaginary->mult(d_eigs_exp_imaginary);
707  *d_phi_mult_eigs_real -= *d_phi_mult_eigs_real_2;
708  Matrix* d_phi_mult_eigs_imaginary = d_phi_real->mult(d_eigs_exp_imaginary);
709  Matrix* d_phi_mult_eigs_imaginary_2 = d_phi_imaginary->mult(d_eigs_exp_real);
710  *d_phi_mult_eigs_imaginary += *d_phi_mult_eigs_imaginary_2;
711 
712  delete d_eigs_exp_real;
713  delete d_eigs_exp_imaginary;
714  delete d_phi_mult_eigs_real_2;
715  delete d_phi_mult_eigs_imaginary_2;
716 
717  return std::pair<Matrix*,Matrix*>(d_phi_mult_eigs_real,
718  d_phi_mult_eigs_imaginary);
719 }
720 
721 double
723 {
724  return d_t_offset;
725 }
726 
727 const Matrix*
729 {
731 }
732 
733 const Matrix*
734 DMD::createSnapshotMatrix(std::vector<Vector*> snapshots)
735 {
736  CAROM_VERIFY(snapshots.size() > 0);
737  CAROM_VERIFY(snapshots[0]->dim() > 0);
738  for (int i = 0 ; i < snapshots.size() - 1; i++)
739  {
740  CAROM_VERIFY(snapshots[i]->dim() == snapshots[i + 1]->dim());
741  CAROM_VERIFY(snapshots[i]->distributed() == snapshots[i + 1]->distributed());
742  }
743 
744  Matrix* snapshot_mat = new Matrix(snapshots[0]->dim(), snapshots.size(),
745  snapshots[0]->distributed());
746 
747  for (int i = 0; i < snapshots[0]->dim(); i++)
748  {
749  for (int j = 0; j < snapshots.size(); j++)
750  {
751  snapshot_mat->item(i, j) = snapshots[j]->item(i);
752  }
753  }
754 
755  return snapshot_mat;
756 }
757 
758 void
759 DMD::load(std::string base_file_name)
760 {
761  CAROM_ASSERT(!base_file_name.empty());
762 
763  char tmp[100];
764  std::string full_file_name = base_file_name;
765  HDFDatabase database;
766  database.open(full_file_name, "r");
767 
768  sprintf(tmp, "dt");
769  database.getDouble(tmp, d_dt);
770 
771  sprintf(tmp, "t_offset");
772  database.getDouble(tmp, d_t_offset);
773 
774  sprintf(tmp, "k");
775  database.getInteger(tmp, d_k);
776 
777  sprintf(tmp, "num_eigs");
778  int num_eigs;
779  database.getInteger(tmp, num_eigs);
780 
781  std::vector<double> eigs_real;
782  std::vector<double> eigs_imag;
783 
784  sprintf(tmp, "eigs_real");
785  eigs_real.resize(num_eigs);
786  database.getDoubleArray(tmp, &eigs_real[0], num_eigs);
787 
788  sprintf(tmp, "eigs_imag");
789  eigs_imag.resize(num_eigs);
790  database.getDoubleArray(tmp, &eigs_imag[0], num_eigs);
791 
792  for (int i = 0; i < num_eigs; i++)
793  {
794  d_eigs.push_back(std::complex<double>(eigs_real[i], eigs_imag[i]));
795  }
796  database.close();
797 
798  full_file_name = base_file_name + "_basis";
799  d_basis = new Matrix();
800  d_basis->read(full_file_name);
801 
802  full_file_name = base_file_name + "_A_tilde";
803  d_A_tilde = new Matrix();
804  d_A_tilde->read(full_file_name);
805 
806  full_file_name = base_file_name + "_phi_real";
807  d_phi_real = new Matrix();
808  d_phi_real->read(full_file_name);
809 
810  full_file_name = base_file_name + "_phi_imaginary";
811  d_phi_imaginary = new Matrix();
812  d_phi_imaginary->read(full_file_name);
813 
814  full_file_name = base_file_name + "_phi_real_squared_inverse";
816  d_phi_real_squared_inverse->read(full_file_name);
817 
818  full_file_name = base_file_name + "_phi_imaginary_squared_inverse";
820  d_phi_imaginary_squared_inverse->read(full_file_name);
821 
822  full_file_name = base_file_name + "_projected_init_real";
824  d_projected_init_real->read(full_file_name);
825 
826  full_file_name = base_file_name + "_projected_init_imaginary";
828  d_projected_init_imaginary->read(full_file_name);
829 
830  full_file_name = base_file_name + "_state_offset";
831  if (Utilities::file_exist(full_file_name + ".000000"))
832  {
833  d_state_offset = new Vector();
834  d_state_offset->read(full_file_name);
835  }
836 
837  d_init_projected = true;
838  d_trained = true;
839 
840  MPI_Barrier(MPI_COMM_WORLD);
841 }
842 
843 void
844 DMD::load(const char* base_file_name)
845 {
846  load(std::string(base_file_name));
847 }
848 
849 void
850 DMD::save(std::string base_file_name)
851 {
852  CAROM_ASSERT(!base_file_name.empty());
853  CAROM_VERIFY(d_trained);
854 
855  if (d_rank == 0)
856  {
857  char tmp[100];
858  std::string full_file_name = base_file_name;
859  HDFDatabase database;
860  database.create(full_file_name);
861 
862  sprintf(tmp, "dt");
863  database.putDouble(tmp, d_dt);
864 
865  sprintf(tmp, "t_offset");
866  database.putDouble(tmp, d_t_offset);
867 
868  sprintf(tmp, "k");
869  database.putInteger(tmp, d_k);
870 
871  sprintf(tmp, "num_eigs");
872  database.putInteger(tmp, d_eigs.size());
873 
874  std::vector<double> eigs_real;
875  std::vector<double> eigs_imag;
876 
877  for (int i = 0; i < d_eigs.size(); i++)
878  {
879  eigs_real.push_back(d_eigs[i].real());
880  eigs_imag.push_back(d_eigs[i].imag());
881  }
882 
883  sprintf(tmp, "eigs_real");
884  database.putDoubleArray(tmp, &eigs_real[0], eigs_real.size());
885 
886  sprintf(tmp, "eigs_imag");
887  database.putDoubleArray(tmp, &eigs_imag[0], eigs_imag.size());
888  database.close();
889  }
890 
891  std::string full_file_name;
892 
893  if (d_basis != NULL)
894  {
895  full_file_name = base_file_name + "_basis";
896  d_basis->write(full_file_name);
897  }
898 
899  if (d_A_tilde != NULL)
900  {
901  full_file_name = base_file_name + "_A_tilde";
902  d_A_tilde->write(full_file_name);
903  }
904 
905  full_file_name = base_file_name + "_phi_real";
906  d_phi_real->write(full_file_name);
907 
908  full_file_name = base_file_name + "_phi_imaginary";
909  d_phi_imaginary->write(full_file_name);
910 
911  full_file_name = base_file_name + "_phi_real_squared_inverse";
912  d_phi_real_squared_inverse->write(full_file_name);
913 
914  full_file_name = base_file_name + "_phi_imaginary_squared_inverse";
915  d_phi_imaginary_squared_inverse->write(full_file_name);
916 
917  full_file_name = base_file_name + "_projected_init_real";
918  d_projected_init_real->write(full_file_name);
919 
920  full_file_name = base_file_name + "_projected_init_imaginary";
921  d_projected_init_imaginary->write(full_file_name);
922 
923  if (d_state_offset != NULL)
924  {
925  full_file_name = base_file_name + "_state_offset";
926  d_state_offset->write(full_file_name);
927  }
928 
929  MPI_Barrier(MPI_COMM_WORLD);
930 }
931 
932 void
933 DMD::save(const char* base_file_name)
934 {
935  save(std::string(base_file_name));
936 }
937 
938 void
939 DMD::summary(std::string base_file_name)
940 {
941  if (d_rank == 0)
942  {
943  CSVDatabase* csv_db(new CSVDatabase);
944 
945  csv_db->putDoubleVector(base_file_name + "_singular_value.csv", d_sv,
947  csv_db->putComplexVector(base_file_name + "_eigenvalue.csv", d_eigs,
948  d_eigs.size());
949 
950  delete csv_db;
951  }
952 }
953 
954 }
void putDoubleVector(const std::string &file_name, const std::vector< double > &data, int nelements, const bool distributed=false) override
Writes a vector of doubles associated with the supplied filename to the currently open CSV database f...
Definition: CSVDatabase.cpp:94
void putComplexVector(const std::string &file_name, const std::vector< std::complex< double >> &data, int nelements)
Writes a vector of complex doubles associated with the supplied filename.
std::pair< Matrix *, Matrix * > phiMultEigs(double t, int deg=0)
Internal function to multiply d_phi with the eigenvalues.
Definition: DMD.cpp:689
Matrix * d_phi_real
The real part of d_phi.
Definition: DMD.h:418
std::vector< std::complex< double > > d_eigs
A vector holding the complex eigenvalues of the eigenmodes.
Definition: DMD.h:448
Matrix * d_phi_real_squared_inverse
The real part of d_phi_squared_inverse.
Definition: DMD.h:428
virtual ~DMD()
Destroy the DMD object.
Definition: DMD.cpp:130
double d_energy_fraction
The energy fraction used to obtain the DMD modes.
Definition: DMD.h:393
int d_dim
The total dimension of the sample vector.
Definition: DMD.h:343
Vector * predict(double t, int deg=0)
Predict state given a time. Uses the projected initial condition of the training dataset (the first c...
Definition: DMD.cpp:645
virtual void train(double energy_fraction, const Matrix *W0=NULL, double linearity_tol=0.0)
Train the DMD model with energy fraction criterion.
Definition: DMD.cpp:203
bool d_init_projected
Whether the initial condition has been projected.
Definition: DMD.h:378
Matrix * d_A_tilde
A_tilde.
Definition: DMD.h:413
virtual void takeSample(double *u_in, double t)
Sample the new state, u_in. Any samples in d_snapshots taken at the same or later time will be erased...
Definition: DMD.cpp:159
virtual void addOffset(Vector *&result, double t=0.0, int deg=0)
Add the appropriate offset when predicting the solution.
Definition: DMD.cpp:674
virtual std::complex< double > computeEigExp(std::complex< double > eig, double t)
Compute the appropriate exponential function when predicting the solution.
Definition: DMD.cpp:683
const Matrix * createSnapshotMatrix(std::vector< Vector * > snapshots)
Get the snapshot matrix contained within d_snapshots.
Definition: DMD.cpp:734
void constructDMD(const Matrix *f_snapshots, int rank, int num_procs, const Matrix *W0, double linearity_tol)
Construct the DMD object.
Definition: DMD.cpp:286
void projectInitialCondition(const Vector *init, double t_offset=-1.0)
Project new initial condition using d_phi. Calculate pinv(phi) x init, or more precisely,...
Definition: DMD.cpp:541
void summary(std::string base_file_name)
Output the DMD record in CSV files.
Definition: DMD.cpp:939
virtual std::pair< Matrix *, Matrix * > computeDMDSnapshotPair(const Matrix *snapshots)
Returns a pair of pointers to the minus and plus snapshot matrices.
Definition: DMD.cpp:227
double d_dt
The time step size between samples.
Definition: DMD.h:348
Vector * d_state_offset
State offset in snapshot.
Definition: DMD.h:368
virtual void save(std::string base_file_name)
Save the object state to a file.
Definition: DMD.cpp:850
const Matrix * getSnapshotMatrix()
Get the snapshot matrix contained within d_snapshots.
Definition: DMD.cpp:728
std::vector< double > d_sv
std::vector holding the signular values.
Definition: DMD.h:388
virtual void setOffset(Vector *offset_vector, int order)
Set the offset of a certain order.
Definition: DMD.cpp:151
std::vector< Vector * > d_sampled_times
The stored times of each sample.
Definition: DMD.h:363
double getTimeOffset() const
Get the time offset contained within d_t_offset.
Definition: DMD.cpp:722
int d_num_singular_vectors
The maximum number of singular vectors.
Definition: DMD.h:383
Matrix * d_phi_imaginary
The imaginary part of d_phi.
Definition: DMD.h:423
double d_t_offset
The time offset of the first sample.
Definition: DMD.h:353
Vector * d_projected_init_real
The real part of the projected initial condition.
Definition: DMD.h:438
int d_rank
The rank of the process this object belongs to.
Definition: DMD.h:333
std::vector< Vector * > d_snapshots
std::vector holding the snapshots.
Definition: DMD.h:358
DMD()
Unimplemented default constructor.
Matrix * d_basis
The left singular vector basis.
Definition: DMD.h:403
int d_num_procs
The number of processors being run on.
Definition: DMD.h:338
bool d_alt_output_basis
Whether to use the alternative basis for output, i.e. phi = U^(+)*V*Omega^(-1)*X.
Definition: DMD.h:408
virtual void load(std::string base_file_name)
Load the object state from a file.
Definition: DMD.cpp:759
Matrix * d_phi_imaginary_squared_inverse
The imaginary part of d_phi_squared_inverse.
Definition: DMD.h:433
Vector * d_projected_init_imaginary
The imaginary part of the projected initial condition.
Definition: DMD.h:443
bool d_trained
Whether the DMD has been trained or not.
Definition: DMD.h:373
virtual void computePhi(DMDInternal dmd_internal_obj)
Compute phi.
Definition: DMD.cpp:260
int d_k
The number of columns used after obtaining the SVD decomposition.
Definition: DMD.h:398
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 putDouble(const std::string &key, double data)
Writes a double associated with the supplied key to currently open database file.
Definition: Database.h:128
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
void getDouble(const std::string &key, double &data)
Reads a double associated with the supplied key from the currently open database file.
Definition: Database.h:215
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 getDoubleArray(const std::string &key, double *data, int nelements, const bool distributed=false)
Reads an array of doubles associated with the supplied key from the currently open HDF5 database file...
virtual void putDoubleArray(const std::string &key, const double *const data, int nelements, const bool distributed=false)
Writes an array of doubles associated with the supplied key to the currently open HDF5 database file.
virtual bool close()
Closes the currently open HDF5 database file.
double * getData() const
Get the matrix data as a pointer.
Definition: Matrix.h:1107
bool distributed() const
Returns true if the Matrix is distributed.
Definition: Matrix.h:177
void write(const std::string &base_file_name) const
write Matrix into (a) HDF file(s).
Definition: Matrix.cpp:937
int numColumns() const
Returns the number of columns in the Matrix. This method will return the same value from each process...
Definition: Matrix.h:220
int numDistributedRows() const
Returns the number of rows of the Matrix across all processors.
Definition: Matrix.h:205
void orthogonalize(bool double_pass=false, double zero_tol=1.0e-15)
Orthonormalizes the matrix.
Definition: Matrix.cpp:1803
Matrix * transposeMult(const Matrix &other) const
Multiplies the transpose of this Matrix with other and returns the product, reference version.
Definition: Matrix.h:642
Matrix * mult(const Matrix &other) const
Multiplies this Matrix with other and returns the product, reference version.
Definition: Matrix.h:283
const double & item(int row, int col) const
Const Matrix member access. Matrix data is stored in row-major format.
Definition: Matrix.h:1007
int numRows() const
Returns the number of rows of the Matrix on this processor.
Definition: Matrix.h:194
void read(const std::string &base_file_name)
read Matrix into (a) HDF file(s).
Definition: Matrix.cpp:969
void read(const std::string &base_file_name)
read Vector from (a) HDF file(s).
Definition: Vector.cpp:492
Vector * minus(const Vector &other) const
Subtracts other and this and returns the result, reference version.
Definition: Vector.h:538
void write(const std::string &base_file_name)
write Vector into (a) HDF file(s).
Definition: Vector.cpp:446
Vector * plus(const Vector &other) const
Adds other and this and returns the result, reference version.
Definition: Vector.h:349
const double & item(int i) const
Const Vector member access.
Definition: Vector.h:656
double inner_product(const Vector &other) const
Inner product, reference form.
Definition: Vector.cpp:222
int dim() const
Returns the dimension of the Vector on this processor.
Definition: Vector.h:266
struct ComplexEigenPair NonSymmetricRightEigenSolve(Matrix *A)
Computes the eigenvectors/eigenvalues of an NxN real nonsymmetric matrix.
Definition: Matrix.cpp:2203
Matrix * ev_real
The real component of the eigenvectors in matrix form.
Definition: Matrix.h:1376
std::vector< std::complex< double > > eigs
The corresponding complex eigenvalues.
Definition: Matrix.h:1386
Matrix * ev_imaginary
The imaginary component of the eigenvectors in matrix form.
Definition: Matrix.h:1381
ComplexEigenPair * eigenpair
The resultant DMD eigenvalues and eigenvectors.
Definition: DMD.h:64
Matrix * snapshots_out
The snapshot vectors of output.
Definition: DMD.h:44
Matrix * basis
The left singular vector basis.
Definition: DMD.h:49
Matrix * basis_right
The right singular vector basis.
Definition: DMD.h:54
Matrix * S_inv
The inverse of singular values.
Definition: DMD.h:59
static bool file_exist(const std::string &filename)
Verifies if a file exists.
Definition: Utilities.cpp:70