libROM  v1.0
Data-driven physical simulation library
BasisGenerator.cpp
1 
11 // Description: The abstract wrapper class for an abstract SVD algorithm and
12 // sampler. This class provides interfaces to each so that an
13 // application only needs to instantiate one concrete
14 // implementation of this class to control all aspects of basis
15 // vector generation.
16 
17 #include "BasisGenerator.h"
18 #include "svd/StaticSVD.h"
19 #include "svd/RandomizedSVD.h"
20 #include "svd/IncrementalSVDStandard.h"
21 #include "svd/IncrementalSVDFastUpdate.h"
22 #include "svd/IncrementalSVDBrand.h"
23 
24 #include <iomanip>
25 #include <fstream>
26 
27 namespace CAROM {
28 
29 BasisGenerator::BasisGenerator(
30  Options options,
31  bool incremental,
32  const std::string& basis_file_name,
33  Database::formats file_format) :
34  d_dim(options.dim),
35  d_incremental(incremental),
36  d_basis_writer(0),
37  d_basis_reader(0),
38  d_write_snapshots(options.write_snapshots)
39 {
40  CAROM_VERIFY(options.dim > 0);
41  CAROM_VERIFY(options.max_num_samples > 0);
42  CAROM_VERIFY(options.singular_value_tol >= 0);
43  CAROM_VERIFY(options.max_basis_dimension > 0);
44  if (incremental)
45  {
46  CAROM_VERIFY(options.linearity_tol > 0.0);
47  CAROM_VERIFY(options.initial_dt > 0.0);
48  CAROM_VERIFY(options.sampling_tol > 0.0);
49  CAROM_VERIFY(options.max_time_between_samples > 0.0);
50  CAROM_VERIFY(options.min_sampling_time_step_scale >= 0.0);
51  CAROM_VERIFY(options.sampling_time_step_scale >= 0.0);
52  CAROM_VERIFY(options.max_sampling_time_step_scale >= 0.0);
53  CAROM_VERIFY(options.min_sampling_time_step_scale <=
55  }
56 
57  if (!basis_file_name.empty()) {
58  d_basis_writer = new BasisWriter(*this, basis_file_name, file_format);
59  }
60  d_update_right_SV = options.update_right_SV;
61  if (incremental)
62  {
63  d_tol = options.sampling_tol;
64  d_max_time_between_samples = options.max_time_between_samples;
65  d_min_sampling_time_step_scale = options.min_sampling_time_step_scale;
66  d_sampling_time_step_scale = options.sampling_time_step_scale;
67  d_max_sampling_time_step_scale = options.max_sampling_time_step_scale;
68  d_dt = options.initial_dt;
69  d_next_sample_time = 0.0;
70 
71  if (options.fast_update_brand) {
72  d_svd.reset(
74  options, basis_file_name));
75  }
76  else if (options.fast_update) {
77  d_svd.reset(
79  options, basis_file_name));
80  }
81  else {
82  d_svd.reset(
84  options, basis_file_name));
85  }
86 
87  // Get the number of processors.
88  int mpi_init;
89  MPI_Initialized(&mpi_init);
90  if (mpi_init) {
91  MPI_Comm_size(MPI_COMM_WORLD, &d_num_procs);
92  }
93  else {
94  d_num_procs = 1;
95  }
96  }
97  else
98  {
99  if (options.randomized) {
100  d_svd.reset(new RandomizedSVD(options));
101  }
102  else {
103  d_svd.reset(new StaticSVD(options));
104  }
105  }
106 }
107 
108 bool
110  double time)
111 {
112  CAROM_VERIFY(time >= 0.0);
113  if (d_incremental)
114  {
115  if(d_update_right_SV)
116  return true;
117  else
118  return time >= d_next_sample_time;
119  }
120 
121  return true;
122 }
123 
124 bool
126  double* u_in,
127  bool add_without_increase)
128 {
129  CAROM_VERIFY(u_in != 0);
130  CAROM_VERIFY(d_svd->getNumSamples() < d_svd->getMaxNumSamples());
131 
132  // Check that u_in is not non-zero.
133  Vector u_vec(u_in, getDim(), true);
134  if (u_vec.norm() == 0.0) {
135  printf("WARNING: BasisGenerator::takeSample skipped trivial sample.\n");
136  return false;
137  }
138 
139  return d_svd->takeSample(u_in, add_without_increase);
140 }
141 
142 void
143 BasisGenerator::loadSampleRange(const std::string& base_file_name,
144  const std::string& kind,
145  int col_min,
146  int col_max,
147  Database::formats db_format)
148 {
149  CAROM_ASSERT(!base_file_name.empty());
150  CAROM_VERIFY(kind == "basis" || kind == "snapshot");
151 
152  if (d_basis_reader) delete d_basis_reader;
153 
154  d_basis_reader = new BasisReader(base_file_name, db_format, d_dim);
155  std::unique_ptr<const Matrix> mat;
156  std::unique_ptr<const Vector> singular_vals;
157 
158  if (kind == "basis") {
160  singular_vals = d_basis_reader->getSingularValues();
161  }
162  else if (kind == "snapshot") {
164  }
165 
166  int num_rows = mat->numRows();
167  int num_cols = mat->numColumns();
168  if (col_min < 0) col_min = 0;
169  if (col_max > num_cols-1) col_max = num_cols-1;
170 
171  CAROM_VERIFY(col_max >= col_min);
172 
173  for (int j = col_min; j <= col_max; j++) {
174  double* u_in = new double[num_rows];
175  for (int i = 0; i < num_rows; i++) {
176  if (kind == "basis") {
177  u_in[i] = mat->item(i,j) * singular_vals->item(j);
178  }
179  else {
180  u_in[i] = mat->item(i,j);
181  }
182  }
183  d_svd->takeSample(u_in, false);
184  delete[] u_in;
185  }
186 }
187 
188 void
189 BasisGenerator::loadSamples(const std::string& base_file_name,
190  const std::string& kind,
191  int cutoff,
192  Database::formats db_format)
193 {
194  loadSampleRange(base_file_name, kind, 0, cutoff-1, db_format);
195 }
196 
197 double
199  double* u_in,
200  double* rhs_in,
201  double time)
202 {
203  CAROM_VERIFY(u_in != 0);
204  CAROM_VERIFY(rhs_in != 0);
205  CAROM_VERIFY(time >= 0);
206  if (d_incremental)
207  {
208 
209  // Check that u_in is not non-zero.
210  int dim = getDim();
211  Vector u_vec(u_in, dim, true);
212  if (u_vec.norm() == 0.0) {
213  return d_next_sample_time;
214  }
215 
216  // Get the current basis vectors.
217  std::shared_ptr<const Matrix> basis = getSpatialBasis();
218 
219  // Compute l = basis' * u
220  Vector l(basis->numColumns(), false);
221  basis->transposeMult(u_vec, l);
222 
223  // basisl = basis * l
224  Vector basisl(basis->numRows(), true);
225  basis->mult(l, basisl);
226 
227  // Compute u - basisl.
228  std::unique_ptr<Vector> eta = u_vec.minus(basisl);
229 
230  // Compute l = basis' * rhs
231  Vector rhs_vec(rhs_in, dim, true);
232  basis->transposeMult(rhs_vec, l);
233 
234  // basisl = basis * l
235  basis->mult(l, basisl);
236 
237  // Compute rhs - basisl.
238  std::unique_ptr<Vector> eta_dot = rhs_vec.minus(basisl);
239 
240  // Compute the l-inf norm of eta + d_dt*eta_dot.
241  double global_norm;
242  double local_norm = 0.0;
243  for (int i = 0; i < dim; ++i) {
244  double val = fabs(eta->item(i) + d_dt*eta_dot->item(i));
245  if (val > local_norm) {
246  local_norm = val;
247  }
248  }
249  if (d_num_procs == 1) {
250  global_norm = local_norm;
251  }
252  else {
253  MPI_Allreduce(&local_norm,
254  &global_norm,
255  1,
256  MPI_DOUBLE,
257  MPI_MAX,
258  MPI_COMM_WORLD);
259  }
260 
261  // Compute dt from this norm.
262  double tmp = d_sampling_time_step_scale*sqrt(d_tol/global_norm);
263  if (tmp < d_min_sampling_time_step_scale) {
264  d_dt *= d_min_sampling_time_step_scale;
265  }
266  else if (tmp > d_max_sampling_time_step_scale) {
267  d_dt *= d_max_sampling_time_step_scale;
268  }
269  else {
270  d_dt *= tmp;
271  }
272  if (d_dt < 0) {
273  d_dt = 0.0;
274  }
275  else if (d_dt > d_max_time_between_samples) {
276  d_dt = d_max_time_between_samples;
277  }
278 
279  // Return next sample time.
280  d_next_sample_time = time + d_dt;
281  return d_next_sample_time;
282  }
283 
284  return time;
285 }
286 
287 void
288 BasisGenerator::resetDt(
289  double new_dt)
290 {
291  if (d_incremental)
292  {
293  d_dt = new_dt;
294  }
295 }
296 
297 void
299  const double energyFractionThreshold,
300  int & cutoff,
301  const std::string & cutoffOutputPath,
302  const int first_sv, const bool squareSV)
303 {
304  const int rom_dim = getSpatialBasis()->numColumns();
305  std::shared_ptr<const Vector> sing_vals = getSingularValues();
306 
307  CAROM_VERIFY(rom_dim <= sing_vals->dim());
308 
309  double sum = 0.0;
310  for (int sv = first_sv; sv < sing_vals->dim(); ++sv) {
311  const double s = (*sing_vals)(sv);
312  sum += squareSV ? s * s : s;
313  }
314 
315  int p = std::floor(-std::log10(energyFractionThreshold));
316  std::vector<double> energy_fractions(p);
317 
318  for (int i = 0; i < p; ++i) {
319  energy_fractions[i] = 1 - std::pow(10, -1 - i);
320  }
321 
322  cutoff = first_sv;
323  bool reached_cutoff = false;
324  double partialSum = 0.0;
325  int count = 0;
326 
327  std::ostream* output_stream;
328 
329  if (!cutoffOutputPath.empty()) {
330  output_stream = new std::ofstream(cutoffOutputPath);
331  } else {
332  output_stream = &std::cout;
333  }
334 
335  for (int sv = first_sv; sv < sing_vals->dim(); ++sv) {
336  const double s = (*sing_vals)(sv);
337  partialSum += squareSV ? s * s : s;
338  for (int i = count; i < p; ++i)
339  {
340  if (partialSum / sum > 1.0 - std::pow(10, -1 - i))
341  {
342  *output_stream << "For energy fraction: 0.";
343  for (int j = 0; j < i+1; ++j) *output_stream << "9";
344  *output_stream << ", take first " << sv+1 << " of "
345  << sing_vals->dim() << " basis vectors" << std::endl;
346  count += 1;
347  }
348  else
349  {
350  break;
351  }
352  }
353  if (!reached_cutoff && partialSum / sum > 1.0 - energyFractionThreshold)
354  {
355  cutoff = sv+1;
356  reached_cutoff = true;
357  }
358  }
359 
360  if (!reached_cutoff) cutoff = sing_vals->dim();
361  *output_stream << std::fixed << std::setprecision(p+1);
362  *output_stream << "For energy fraction: " << 1.0 - energyFractionThreshold <<
363  ", take first "
364  << cutoff << " of " << sing_vals->dim() << " basis vectors" << std::endl;
365 
366  if (!cutoffOutputPath.empty()) {
367  static_cast<std::ofstream*>(output_stream)->close();
368  delete output_stream;
369  }
370 }
371 
373 {
374  if (d_basis_writer) {
375  delete d_basis_writer;
376  }
377  if (d_basis_reader) {
378  delete d_basis_reader;
379  }
380 }
381 
382 }
void loadSampleRange(const std::string &base_file_name, const std::string &kind="basis", int col_min=0, int col_max=1e9, Database::formats db_format=Database::formats::HDF5)
Load previously saved sample (basis or state) within a column range.
void finalSummary(const double energyFractionThreshold, int &cutoff, const std::string &cutoffOutputPath="", const int first_sv=0, const bool squareSV=true)
Prints the summary of recommended numbers of basis vectors.
BasisReader * d_basis_reader
Reader of basis vectors.
bool isNextSample(double time)
Returns true if it is time for the next svd sample.
std::shared_ptr< const Matrix > getSpatialBasis()
Returns the basis vectors for the current time interval as a Matrix.
BasisWriter * d_basis_writer
Writer of basis vectors.
void loadSamples(const std::string &base_file_name, const std::string &kind="basis", int cut_off=1e9, Database::formats db_format=Database::formats::HDF5)
Load previously saved sample (basis or state).
boost::shared_ptr< SVD > d_svd
Pointer to the abstract SVD algorithm object.
std::shared_ptr< const Vector > getSingularValues()
Returns the singular values for the current time interval as a Vector.
~BasisGenerator()
Destructor.
double computeNextSampleTime(double *u_in, double *rhs_in, double time)
Computes next time an svd sample is needed.
bool takeSample(double *u_in, bool add_without_increase=false)
Sample the new state, u_in, at the given time.
std::unique_ptr< Matrix > getSnapshotMatrix()
Returns the snapshot matrix for the requested time.
std::unique_ptr< Matrix > getSpatialBasis()
Returns the spatial basis vectors as a Matrix.
Definition: BasisReader.cpp:76
std::unique_ptr< Vector > getSingularValues()
Returns the singular values for the requested time.
formats
Implemented database file formats. Add to this enum each time a new database format is implemented.
Definition: Database.h:311
bool fast_update_brand
If true use the exact Brand's algorithm for the incremental SVD.
Definition: Options.h:302
double max_time_between_samples
The upper bound on time between samples of the incremental SVD algorithm.
Definition: Options.h:290
int dim
The dimension of the system on this processor.
Definition: Options.h:215
double max_sampling_time_step_scale
The maximum overall scale factor to apply to time step of the incremental SVD algorithm.
Definition: Options.h:341
int max_num_samples
The maximum number of samples.
Definition: Options.h:220
double singular_value_tol
The singular value tolerance used in the SVD algorithm.
Definition: Options.h:240
double linearity_tol
The tolerance of the incremental SVD algorithm to determine whether or not a sample is linearly depen...
Definition: Options.h:271
double sampling_tol
The sampling control tolerance of the incremental SVD algorithm. Limits error in projection of soluti...
Definition: Options.h:284
double min_sampling_time_step_scale
The minimum overall scale factor to apply to time step of the incremental SVD algorithm.
Definition: Options.h:329
bool randomized
Whether to use the randomized SVD algorithm.
Definition: Options.h:252
bool update_right_SV
Whether to update the right singular values.
Definition: Options.h:225
bool fast_update
If true use the fast update version of the incremental SVD algorithm.
Definition: Options.h:296
double initial_dt
The initial simulation time step of the incremental SVD algorithm.
Definition: Options.h:277
int max_basis_dimension
The maximum dimension of the basis.
Definition: Options.h:235
double sampling_time_step_scale
The scaling factor to apply to sampling algorithm of the incremental SVD algorithm.
Definition: Options.h:335
double norm() const
Form the norm of this.
Definition: Vector.cpp:216
std::unique_ptr< Vector > minus(const Vector &other) const
Subtracts other and this and returns the result.
Definition: Vector.h:402